仅用pygame+python实现植物大战僵尸-----完成比完美更重要

news2024/11/26 22:31:28

前言

  • 其实这个项目再我上半年就想着做一下的,但是一直拖到现在,我现在深刻的理解到,不要想那么多,先做,因为永远不可能准备好,都是边做边学便准备的,完成比完美更重要;
  • 使用python,是因为简单,我感觉大多数00后程序员应该都一个实现植物大战僵尸的梦吧;
  • 这一次我深刻体会到了业务的重要性,很多时候对业务不理解,是很难做出点什么东西的,更难成为架构师;
  • 还有,我深刻体会到了一句话:“仅修改少量代码,就实现功能”。

环境

  • python:3.11.7,pygame:2.6.1
  • 编译器:vscode
  • 游戏运行结果截图

在这里插入图片描述

  • 游戏任务图

在这里插入图片描述

  • 游戏架构图

在这里插入图片描述

  • main.py
import pygame
import sys
from pygame.locals import *
from const import *
from game import *

pygame.init()

DS = pygame.display.set_mode((1280, 600))
game = Game(DS)

while True:
    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.MOUSEBUTTONDOWN:
            game.mouseClickHandle(event.button)   # 鼠标事件触发

    DS.fill((255, 255, 255))
    
    game.draw()
    game.update()
    
    pygame.display.update()

  • cosnt.py
# 定义一些常量
GAME_SIZE = (1280, 600)  # 游戏地图大小
# 网格游戏,一些参数
LEFT_TOP = (200, 65)  # 游戏网格,左上角坐标
GRID_SIZE = (76, 96)  # 每一个格子大小
GRID_COUNT = (9, 5)  # 网格数量,9列5行
# 图片路径
PATH_BACK = "pic/other/back.png"
PATH_LOSS = "pic/other/lose.png"

SUNFLOWER_ID = 3
PEASHOOTER_ID = 4

  • data_object.py
# 储存固定参数
data = {
    0 : {   # 豌豆
        'PATH' : 'pic/other/peabullet.png',
        'IMAGE_INDEX_MAX' : 0,   # 图片索引范围
        'IMAGE_INDEX_CD' : 0.0,  # 图片更新频率
        'POSITION_CD' : 0.008,   # 位置更新速度
        'SUMMON_CD' : -1,        # 产生物的CD
        'SIZE' : (44, 44),       # 图片缩放大小
        'SPEED' : (4, 0),        # 速度
        'CAN_LOOT' : False,      # 是否可以捡 
        'PRICE' : 0,             # 价格为0
        'HP' : 1,                # 血量
        'ATT' : 1,               # 攻击力
    },
    
    1 : {   # 僵尸
        'PATH' : 'pic/zombie/0/%d.png',
        'IMAGE_INDEX_MAX' : 15,
        'IMAGE_INDEX_CD' : 0.2,
        'POSITION_CD' : 0.2,
        'SUMMON_CD' : -1, 
        'SIZE' : (100, 128),
        'SPEED' : (-2.5, 0),
        'CAN_LOOT' : False,
        'PRICE' : 0, 
        'HP' : 5,                
        'ATT' : 1,               
    },
    
    2 : {  # 阳光
        'PATH' : 'pic/other/sunlight/%d.png',
        'IMAGE_INDEX_MAX' : 30,
        'IMAGE_INDEX_CD' : 0.06,
        'POSITION_CD' : 0.05,
        'SUMMON_CD' : -1, 
        'SIZE' : (80, 80),
        'SPEED' : (0, 2),
        'CAN_LOOT' : True,
        'PRICE' : 25, 
        'HP' : 1000000000,                
        'ATT' : 0,   
    },
    
    3 : {   # 向日葵
        'PATH' : 'pic/plant/sunflower/%d.png',
        'IMAGE_INDEX_MAX' : 19,
        'IMAGE_INDEX_CD' : 0.07,
        'POSITION_CD' : 10000,
        'SUMMON_CD' : 8, 
        'SIZE' : (128, 128),
        'SPEED' : (0, 0),
        'CAN_LOOT' : False,
        'PRICE' : 50,  
        'HP' : 5,                
        'ATT' : 0,   
    },
    
    4 : {   # 射手
        'PATH' : 'pic/plant/peashooter/%d.png',
        'IMAGE_INDEX_MAX' : 15,
        'IMAGE_INDEX_CD' : 0.15,
        'POSITION_CD' : 10000,
        'SUMMON_CD' : 3, 
        'SIZE' : (128, 128),
        'SPEED' : (0, 0),
        'CAN_LOOT' : False,
        'PRICE' : 100,  
        'HP' : 5,                
        'ATT' : 0,   
    },
}
  • game.py
import pygame
import image
import zombiebase
import peabullet
import data_object
import sunlight
import sunflower
import peashooter
import data_object
import time
import random
from const import *

class Game(object):
    def __init__(self, ds):
        self.ds = ds  
        self.back = image.Image(PATH_BACK, 0, (0, 0), GAME_SIZE, 0)  # 储存背景
        self.loss = image.Image(PATH_LOSS, 0, (0, 0), GAME_SIZE, 0)  # 游戏结束
        self.isGameOver = False
        self.plants = []
        self.summons = []
        self.zombies = []
        
        self.allPrivce = 100   # 初始价格
        
        self.priveFont = pygame.font.Font(None, 60)  # 字体
        
        self.zombieGenerateTime = 0   # 上一次生成僵尸的时间
        
        # 打僵尸几分
        self.zombie = 0
        self.zombieFont = pygame.font.Font(None, 60)
        
        self.hasPlant = []
        for i in range(GRID_SIZE[0]):   # 赋值的是一个格子的
            col = []
            for j in range(GRID_SIZE[1]):
                col.append(0)
            self.hasPlant.append(col)
        
    # 得到要种植的坐标
    def getIndexByPos(self, pos):    # 得到x,y坐标(注意是那种压缩的,就是x * 每个格子宽度 == 真实位置)
        x = (pos[0] - LEFT_TOP[0]) // GRID_SIZE[0]
        y = (pos[1] - LEFT_TOP[1]) // GRID_SIZE[1]
        return x, y
    
    def renderFont(self):
        textImage = self.priveFont.render("Glod: " + str(self.allPrivce), True, (0, 0, 0))
        self.ds.blit(textImage, (13, 23))
        
        textImage = self.priveFont.render("Glod: " + str(self.allPrivce), True, (255, 255, 255))
        self.ds.blit(textImage, (10, 20))
        
        textImage = self.zombieFont.render("Score: " + str(self.zombie), True, (0, 0, 0))
        self.ds.blit(textImage, (13, 83))
        
        textImage = self.zombieFont.render("Score: " + str(self.zombie), True, (255, 255, 255))
        self.ds.blit(textImage, (10, 80))
        
        
    def draw(self):
        self.back.draw(self.ds)
        for plant in self.plants:    # 植物绘制
            plant.draw(self.ds)
        for summon in self.summons:   # 生成物绘制
            summon.draw(self.ds)
        for zombie in self.zombies:
            zombie.draw(self.ds)
        
        # 绘制金额
        self.renderFont()
        
        # 是否结束
        if self.isGameOver:
            self.loss.draw(self.ds)
                       
    def update(self):
        self.back.update()
        for plant in self.plants:
            plant.update()
            if plant.hasSummon():   # 有生成物
                summ = plant.doSummon()   # 就生成
                self.summons.append(summ)   # 给game管理生命周期
        for summon in self.summons:
            summon.update()
        
        for zombie in self.zombies:
            zombie.update()
            
        # 更新一次,看是否能产生僵尸
        if time.time() - self.zombieGenerateTime > 10:
            self.zombieGenerateTime = time.time()
            self.addZombie(14, random.randint(0, 4))
            
        self.checkSummonVsZombie()
        self.checkZombieVsPlant()
        
        # 游戏是否结束
        for z in self.zombies:
            if z.getRect().x < 0:
                self.isGameOver = True
        
        # 子弹超出屏幕,需要销毁
        for summon in self.summons:
            if summon.getRect().x > GAME_SIZE[0] or summon.getRect().y > GAME_SIZE[1]:
                self.summons.remove(summon)
                break  # 退出是因为[]索引会改变
        
    # 僵尸和植物对抗
    def checkSummonVsZombie(self):
        for summon in self.summons:
            for zombie in self.zombies:
                if summon.isCollide(zombie):   # 僵尸和植物对抗
                    self.fight(summon, zombie)  # 对抗
                    if zombie.hp <= 0:
                        self.zombies.remove(zombie)   # 移除僵尸
                        self.zombie += 1   # 加分
                    if summon.hp <= 0:
                        self.summons.remove(summon)   # 移除植物
                    return 
        
    # 僵尸吃植物
    def checkZombieVsPlant(self):
        for zombie in self.zombies:
            for plant in self.plants:
                if zombie.isCollide(plant):
                    self.fight(zombie, plant)
                    if plant.hp <= 0:
                        self.plants.remove(plant)
                        break
            
            
    # 产生阳光
    def addSunFlower(self, i, j):
        pos = LEFT_TOP[0] + i * GRID_SIZE[0], LEFT_TOP[1] + j * GRID_SIZE[1]
        sf = sunflower.SunFlower(3, pos)
        self.plants.append(sf)
        
    # 产生豌豆
    def addPeaShooter(self, x, y):
        pos = LEFT_TOP[0] + x * GRID_SIZE[0], LEFT_TOP[1] + y * GRID_SIZE[1]
        sf = peashooter.PeaShooter(PEASHOOTER_ID, pos)
        self.plants.append(sf)
    
    # 产生僵尸
    def addZombie(self, x, y):
        pos = LEFT_TOP[0] + x * GRID_SIZE[0], LEFT_TOP[1] + y * GRID_SIZE[1]
        zom = zombiebase.ZombieBase(1, pos)
        self.zombies.append(zom)
        
    
    # 对抗
    def fight(self, a, b):
        while True:
            a.hp -= b.attack
            b.hp -= a.attack
            if b.hp <= 0:    # a 打败 b
                return True 
            if a.hp <= 0:    # b 打败 a
                return False
        
        return False
    
    
    def checkLoot(self, mousePos):
        for summon in self.summons:
            if not summon.getIsLoot():
                continue 
            rect = summon.getRect()  # 获取图片矩形
            if rect.collidepoint(mousePos):   # 点击坐标是否在举行区域内
                self.summons.remove(summon)   # 移除内存
                
                self.allPrivce += summon.getPrice()   # 金额增加
                
                return True 
        
        return False
                
    
    # 种
    def checkAddPlant(self, mousePos, objjId):
        x, y = self.getIndexByPos(mousePos)
        
        # 判断是否能种植
        if x < 0 or x >= GRID_COUNT[0]:
            return 
        if y < 0 or y >= GRID_COUNT[1]:
            return
        
        # 不能重复种种植判断
        if self.hasPlant[x][y] == 1:
            return
        self.hasPlant[x][y] = 1
        
        # 金币扣除
        if self.allPrivce < data_object.data[objjId]['PRICE']:
            return
        self.allPrivce -= data_object.data[objjId]['PRICE']
        
        if objjId == SUNFLOWER_ID:    # 种花
            self.addSunFlower(x, y)
        elif objjId == PEASHOOTER_ID:  # 种射手
            self.addPeaShooter(x, y)
    
    # 鼠标事件
    def mouseClickHandle(self, btn):
        # 游戏结束,鼠标不能种植
        if self.isGameOver:
            return 
        
        mousePos = pygame.mouse.get_pos()  # 获取鼠标位置
        if self.checkLoot(mousePos):           # 触发,不能再种其他东西了
            return 
        
        if btn == 1:   # 鼠标左键
            self.checkAddPlant(mousePos, SUNFLOWER_ID)
        elif btn == 3:
            self.checkAddPlant(mousePos, PEASHOOTER_ID)
        
  • image.py
import pygame


class Image(pygame.sprite.Sprite):
    def __init__(self, pathFmt, pathIndex, pos, size=None, pathIndexCount=0):
        self.pathFmt = pathFmt
        self.pathIndex = pathIndex  # 存储索引
        self.pos = list(pos)  # ()元组不支持修改,但list可以,这里可以修改坐标
        self.size = size  # 窗口大小
        self.pathIndexCount = pathIndexCount  # 储存图片索引最大下标
        self.updateImage()  # 显示图片

    # 更新图片
    def updateImage(self):
        path = self.pathFmt
        if self.pathIndexCount != 0:  # 更新图片目录
            path = path % self.pathIndex
        self.image = pygame.image.load(path)  # 更新图片,贴图
        if self.size:  # 有大小,则缩放
            self.image = pygame.transform.scale(self.image, self.size)

    # 更新图片大小
    def updateSize(self):
        self.size = size
        self.updateImage()

    # 更新图片索引
    def updateIndex(self, pathIndex):
        self.pathIndex = pathIndex
        self.updateImage()  # 索引更新,则更新图片

    # 获取图片大小和坐标
    def getRect(self):
        rect = self.image.get_rect()
        rect.x, rect.y = self.pos  # 移动本质是图片坐标的更改(左上角)
        return rect

    # 僵尸移动
    def doLeft(self):
        self.pos[0] -= 0.15  # 移动速度,本质是坐标修改

    def draw(self, ds):
        ds.blit(self.image, self.getRect())
       


  • objectbase.py
import image
import time
import data_object

class ObjectBase(image.Image):
    def __init__(self, id, pos):
        # 定义时间,实现自驱动
        self.preTimeIndex = 0
        self.prePositionTime = 0
        self.preSummonTime = 0
        # 储存不同物体的id
        self.id = id
        
        # 血量和攻击力
        self.hp = self.getData()['HP']
        self.attack = self.getData()['ATT']
        
        # 继承
        super(ObjectBase, self).__init__(
            self.getData()['PATH'],
            0,
            pos,
            self.getData()['SIZE'],
            self.getData()['IMAGE_INDEX_MAX']
        )

    # 返回不同的数据
    def getData(self):
        return data_object.data[self.id]
    
    # 返回速度
    def getSpeed(self):
        return self.getData()['SPEED']

    # 返回更新动画的时间
    def getPositionCD(self):
        return self.getData()['POSITION_CD']
    
    # 返回图片更新时间
    def getImageIndexCD(self):
        return self.getData()['IMAGE_INDEX_CD']
    
    # 产生物的时间
    def getSummonCD(self):
        return self.getData()['SUMMON_CD']
    
    # 返回是否可以捡
    def getIsLoot(self):
        return self.getData()['CAN_LOOT']
    
    # 返回植物相应的价格
    def getPrice(self):
        return self.getData()['PRICE']
    
    # 相撞
    def isCollide(self, other):
        return self.getRect().colliderect(other.getRect())  # 相撞

    # 更新动画
    def update(self):
        self.checkImageIndex()  # 更新帧动画
        self.checkPosition()  # 更新图片坐标
        self.checkSummon()
    
    # 是否产生了生成物
    def checkSummon(self):
        if time.time() - self.preSummonTime <= self.getSummonCD():
            return 
        self.preSummonTime = time.time()
        # 调用产生阳光
        self.preSummon()

    def checkImageIndex(self):
        #储存更新时间
        if time.time() - self.preTimeIndex <= self.getImageIndexCD():  # 间隔时间和位置更新一致
            return 
        self.preTimeIndex = time.time()
        
        idx = self.pathIndex + 1
        if idx >= self.pathIndexCount:
            idx = 0
        self.updateIndex(idx)

    def checkPosition(self):
        if  time.time() - self.prePositionTime <= self.getPositionCD():  # 间隔时间和位置更新一致
            return False
        self.prePositionTime = time.time()
        
        speed = self.getSpeed()
        self.pos = (self.pos[0] + speed[0], self.pos[1] + speed[1])
        
        return True
        
    def preSummon(self):
        pass
    
    # 是否有产生物
    def hasSummon(self):
        pass
    
    # 生产产生物
    def doSummon(self):
        pass
  • peabullet.py
import objectbase 

class PeaBullet(objectbase.ObjectBase):
        pass
  • peashooter.py
import objectbase 
import peabullet
import time

class PeaShooter(objectbase.ObjectBase):
    def __init__(self, id, pos):
        super(PeaShooter, self).__init__(id, pos)
        self.hasBullet = False  # 可以发射子弹
        self.hasShoot = False   # 立即发射
    
    def hasSummon(self):
        return self.hasBullet
    
    # 产生阳光
    def preSummon(self):
        self.hasShoot = True        # 发射
        self.pathIndex = 0   # 索引为0
           
    # 种阳光
    def doSummon(self):
        if self.hasSummon():
            self.hasBullet = False 
            return peabullet.PeaBullet(0, (self.pos[0] + 20, self.pos[1] + 30))
        
    
    def checkImageIndex(self):
        #储存更新时间
        if time.time() - self.preTimeIndex <= self.getImageIndexCD():  # 间隔时间和位置更新一致
            return 
        self.preTimeIndex = time.time()
        
        idx = self.pathIndex + 1
        if idx == 8 and self.hasShoot:
            self.hasBullet = True  # 发送
        if idx >= self.pathIndexCount:  # 不发射情况
            idx = 9
        
        self.updateIndex(idx)
  • sunflower.py
import objectbase 
import sunlight

class SunFlower(objectbase.ObjectBase):
        def __init__(self, id, pos):
            super(SunFlower, self).__init__(id, pos)
            self.hasSunlight = False
        
        def hasSummon(self):
            return self.hasSunlight
        
        # 产生阳光
        def preSummon(self):
            self.hasSunlight = True
            

        # 种阳光
        def doSummon(self):
            if self.hasSummon():
                self.hasSunlight = False 
                return sunlight.SunLight(2, (self.pos[0] + 20, self.pos[1] + 10))
  • sunlight.py
import objectbase 

class SunLight(objectbase.ObjectBase): 
        pass
  • zombiebase.py
import objectbase 

class ZombieBase(objectbase.ObjectBase): 
        pass

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2187323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【YOLO学习】YOLOv2详解

文章目录 1. 概述2. Better2.1 Batch Normalization&#xff08;批归一化&#xff09;2.2 High Resolution Classifier&#xff08;高分辨率分类器&#xff09;2.3 Convolutional With Anchor Boxes&#xff08;带有Anchor Boxes的卷积&#xff09;2.4 Dimension Clusters&…

每日一题|1928. 规定时间内到达终点的最小花费|动态规划、最小路径

本题需要使用动态规划进行解决。 分析&#xff1a; 求解最小值而且每一次的状态是由上一次的状态推导出来的&#xff0c;用动态规划。 难点&#xff1a;dp数组的定义和更新。 1、dp数组的定义 在时刻t&#xff0c;位置i处&#xff0c;此时的花费可以表示为如下的形式&#…

CNN卷积神经网络算法原理

全连接神经网络概述 输入层在左点自外&#xff0c;相应的输出层在右点之外&#xff0c; 这就像一个函数&#xff0c;yf(x)&#xff0c;x即输入&#xff0c;f即隐藏层&#xff0c;y即输出&#xff0c;或者是ymodel(x) 全连接神经网络的结构单元 主要是从单元到整体&#xff0c…

杀疯啦!yolov11+strongsort的目标跟踪实现

目录 yolov11介绍——实时端到端物体检测 概述 主要特征 支持的任务和模式 性能指标 总结 strongsort介绍 指标图 系统定位 效果展示 训练与预测 UI设计 界面其他功能展示 完整代码实现UI界面 yolov11介绍——实时端到端物体检测 概述 YOLO11 是 Ultralytics Y…

基于yolov5 无人机检测包含:数据集➕训练好的代码模型训练了300轮 效果看下图 map97%以上

基于yolov5 无人机检测包含:数据集➕训练好的代码模型训练了300轮 效果看下图 map97%以上 基于YOLOv5的无人机检测项目 项目名称 基于YOLOv5的无人机检测 (Drone Detection with YOLOv5) 项目概述 该项目使用YOLOv5模型进行无人机目标检测。数据集包含大量带有标注的无人机…

wsl中安装ubuntu,vscode访问这个ubuntu

WSL1升级为WSL2 wsl --set-default-version 2 wsl --set-version Ubuntu-22.04 2在windows商店中也可以安装ubuntu&#xff0c;在这个ubuntu中windows的c盘在/mnt/c中

国庆刷题(day2)

C语言刷题&#xff1a; C刷题&#xff1a;

数据结构与算法——Java实现 27.双端队列

很多人觉得做一件事付出了10分的努力&#xff0c;却只得到5分的汇报。 其实剩下的五分&#xff0c;是在填补你过往的懒惰。 只有将过往的懒惰填满&#xff0c; 努力才会有正向结果 —— 24.10.3 一、概述 双端队列、队列、栈对比&#xff1a; 队列 一端删除&#xff08;头&am…

计算机毕业设计 基于Python的个性化旅游线路推荐系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

C++基础补充(02)C++其他控制语句break continue goto等

文章目录 1. break2. continue 语句3. goto 语句goto的存在 4. 跳出多重循环4.1 goto 直接跳转4.2 C11及其后版本的 return 语句4.3 使用标志变量 在C中&#xff0c;控制语句用于管理程序的执行流程。常见有 break、continue 和 goto。 1. break break语句主要用于在循环或者s…

【GEE学习第一期】GEE介绍、注册及基本使用

【GEE学习第一期】GEE介绍、注册及基本使用 GEE基本介绍GEE架构编辑语言&#xff1a;JavaScript GEE注册GEE平台界面GEE编辑器API区详解 GEE环境配置本地Python环境配置在线Python环境配置 参考 GEE基本介绍 地球引擎代码编辑器&#xff08;Google Earth Engine, GEE&#xff…

oauth2授权码模式单点登录

文章目录 前言一、单点登录是什么&#xff1f;二、oauth2授权码模式单点登录流程1.流程图2. 代码相关2. 验证流程 总结 前言 oauth2 有四种模式,常用的为密码和授权码,剩下两种几乎不用 密码模式,很好理解,就是根据输入的用户名/密码进行登录认证的,最终返回一个合法token授权…

Coze:如何使用主页对话框?

你好&#xff0c;我是三桥君 我们今天要介绍的功能模块是“主页对话框”。 目录 访问官网 登录首页 基本功能 主页对话框 第一个功能&#xff1a;如何与自己收藏的机器人进行对话&#xff1f; 第二个功能&#xff1a;如何请求主页对话框的机器人帮助创建一个新的机器人&#x…

C++11 异步操作 std::future类

阅读导航 引言一、异步的概念二、应用场景1. 异步任务处理2. 并发控制3. 结果获取 三、使用示例1. 使用std::async关联异步任务&#x1f4bb;示例代码说明 2. 使用std::packaged_task和std::future配合&#xff08;1&#xff09;定义std::packaged_task&#xff08;2&#xff0…

游戏修改器Cheat Engine CE v7.5修改版下载安装详细方法

Cheat Engine是一个专注于游戏的修改器。它可以用来扫描游戏中的内存&#xff0c;并允许修改它们。它还附带了调试器、反汇编器、汇编器、变速器、作弊器生成、Direct3D操作工具、系统检查工具等。 具体安装方法如下&#xff1a; 地址&#xff1a;Cheat Engine 7.5.zip 解压文件…

Prompt 初级版:构建高效对话的基础指南

Prompt 初级版&#xff1a;构建高效对话的基础指南 文章目录 Prompt 初级版&#xff1a;构建高效对话的基础指南一 “标准”提示二 角色提示三 多范例提示四 组合提示五 规范化提示 本文介绍了提示词的基础概念与不同类型&#xff0c;帮助用户更好地理解如何在对话中构建有效的…

Java 计算器项目

更多有趣请关注公众号 计算器项目 代码仓库&#xff1a;https://gitee.com/wengxiulin/vs_code 项目图片 项目简介 这是一个用 Java 编写的简单计算器应用程序&#xff0c;具有基本的数学运算功能。该计算器支持加、减、乘、除等运算&#xff0c;并提供用户友好的图形界面…

CSP-J模拟赛三补题报告

前言 挂了110pts( ⇑ \Uparrow ⇑ \hspace{14em} 有史以来最大傻逼 T1&#xff1a; 100 p t s \color{green}100pts 100pts T2: 100 p t s → 80 p t s \color{green}100pts\color{yellow}\rightarrow\color{red}80pts 100pts→80pts T3: 100 p t s → 10 p t s \color{gre…

java 的三种IO模型(BIO、NIO、AIO)

java 的三种IO模型&#xff08;BIO、NIO、AIO&#xff09; 一、BIO 阻塞式 IO&#xff08;Blocking IO&#xff09;1.1、BIO 工作机制1.2、BIO 实现单发单收1.3、BIO 实现多发多收1.4、BIO 实现客户端服务端多对一1.5、BIO 模式下的端口转发思想 二、NIO 同步非阻塞式 IO&#…

【Godot4.3】模拟平面图形绕轴或点在空间旋转

概述 平面图形&#xff0c;除了常规的线性变换&#xff1a;平移、缩放、旋转、斜切之外。还可以模仿在三维空间旋转、透视等等。 矩形绕纵对称轴旋转实点的轨迹 绕对称旋转是个特殊情况&#xff0c;轨迹是圆也是为了便于理解。更实际的情况应该是椭圆。非对称轴旋转的情况轨…