【pygame】 教程:模拟车辆运动(持续更新)————7/31
本帖最后由 cjjJasonchen 于 2023-7-31 12:08 编辑Python——pygame游戏开发教程:模拟车辆移动
前言:
最近贴住发现论坛里还有网上基本都很难找到做这种的,所以就做了{:10_279:}
本来想等几天再写这个帖子的,没想到预告竟然让大家这么迫不及待。。。{:10_297:}
当然呐,先说一下前言终于开始了吗,
在阅读这篇帖子之前,请注意:
1、请先保证你拥有python入门水平(怎么说也要看完百分之70小甲鱼教程吧~)
2、其次,你需要一定的pygame基础:不说你能写个打飞机,至少你的电脑上要有pygame吧~
3、本节课会涉及到一丢丢的三角函数,作者菜鸡一个,自己弄不太懂,也说不太明白,
所以大家可以看看我写的公式或者查查百度,这节课是游戏开发课不是数学课的说~
4、这篇帖子的阅读和学习可能需要比较长的时间{:10_257:}
5、有些输入法的中文模式可能会阻断pygame接受键盘事件,
请点击shift切换成英文模式,最好在点击”Capslock“大写锁定,防止误触切换成中文{:10_275:}
(大写锁定也不会阻断键盘事件)
好啦~话不多说,教程开始!
教程1:旋转一个方块
好的,我们先看看这一页学完你可以做到什么:
(通过键盘”A“和”D“顺时针旋转)
首先我们需要一个Car类,表示玩家操作的方块,继承pygame.sprite.Spriteclass Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
然后,老规矩,他要有透明度:self.image = pygame.Surface().convert_alpha()
一个需要旋转的Sprite对象,最重要的是:self.o = self.image # 备份原图 :看过小甲鱼教程的都知道,旋转会破坏图片
self.orect = self.o.get_rect() #备份原图位置:图片旋转后位置会发生变化
self.rect = self.orect
然后剩下的一些属性:# 设置位置
self.orect.centerx = 400
self.orect.centery = 300
# 绘制
pygame.draw.rect(self.image,(255,255,255),)
self.angle = 0
self.turn = 0 # 为了方便,在主循环里改
接着,是一个方法,这里看不懂的先别急,往下看,后面会讲:
def update(self):
# 转动
self.angle += self.turn
# 防止角度>360°,方便后面的计算
if self.angle > 360:
self.angle -= 360
# 防止角度<0°, 方便后面的计算
if self.angle < 0:
self.angle += 360
# 得到角度的弧度
self.radius = math.pi/180*self.angle
# 旋转图像,得到新的图像和图像位置
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
# 保证图像中心位置不变
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
相信聪明的鱼油一定可以看懂的对吧~{:10_297:}
https://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.png
接下来我们先把这些连起来,放到大循环里面(大家跑一下试试,然后我们再看看前面的方法):import pygame
import sys
import math
from pygame.locals import *
class Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
self.image = pygame.Surface().convert_alpha()
self.o = self.image # 备份原图 :看过小甲鱼教程的都知道,旋转会破坏图片
self.orect = self.o.get_rect() #备份原图位置:图片旋转后位置会发生变化
self.rect = self.orect
# 设置位置
self.orect.centerx = 400
self.orect.centery = 300
# 绘制
pygame.draw.rect(self.image,(255,255,255),)
self.angle = 0
self.turn = 0 # 为了方便,在主循环里改
self.rigth = False
self.left= False
def update(self):
# 转动
self.angle += self.turn
# 防止角度>360°,方便后面的计算
if self.angle > 360:
self.angle -= 360
# 防止角度<0°, 方便后面的计算
if self.angle < 0:
self.angle += 360
# 得到角度的弧度
self.radius = math.pi/180*self.angle
# 旋转图像,得到新的图像和图像位置
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
# 保证图像中心位置不变
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
if __name__ == "__main__":
size = width, height = 800,600
screen = pygame.display.set_mode(size)
pygame.display.set_caption("title")
clock = pygame.time.Clock()
delay = 60 # 延时计时器
time = 0
# 背景颜色
bg = (85,85,85)
# 是否全屏
fullscreen = False
screen_change = False
running = True
#实例化对象
sprites = pygame.sprite.Group()
car = Car()
sprites.add(car)
while running:
clock.tick(60)
# 检测是否全屏
if fullscreen and screen_change:
screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
screen_change = False
elif screen_change:
screen = pygame.display.set_mode(size)
screen_change = False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print("左键按下")
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
#F11切换全屏
elif event.key == K_F11:
fullscreen = not fullscreen
screen_change = True
elif event.key == K_a:
car.turn = 5
elif event.key == K_d:
car.turn = -5
elif event.key == K_w:
car.w = True
elif event.key == K_s:
car.s = True
elif event.type == KEYUP:
if event.key == K_a:
if car.turn >0:
car.turn = 0
elif event.key == K_d:
if car.turn <0:
car.turn = 0
elif event.key == K_w:
car.w = False
elif event.key == K_s:
car.s = False
#画背景
screen.fill(bg)
#画 xxxx
sprites.draw(screen)
sprites.update()
# 刷新界面
pygame.display.update()
static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.png
现在我们已经达到前面的效果了,我来讲讲前面方法里面大家可能看不懂的地方
# 防止角度>360°,方便后面的计算
if self.angle > 360:
self.angle -= 360
# 防止角度<0°, 方便后面的计算
if self.angle < 0:
self.angle += 360
这里学过用计算器算山叫含树的同学应该可以想到,有很多时候,计算器算负角度转换弧度啊,三角函数的时候可能会出问题。。。虽然不知道什么问题,但这样肯定可以减少错误
https://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.pnghttps://fishc.com.cn/static/image/hrline/line7.png这个弧度有什么用呢?————答案是这里暂时没用,但是当你想要移动这个长方形的时候会用到它,所以先跟他混个眼熟吧~{:10_282:}# 得到角度的弧度
self.radian = math.pi/180*self.angle
# 保证图像中心位置不变
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery那么我们为什么要重新算一边中心位置呢?
给大家演示一下我们先吧透明度注释掉self.image = pygame.Surface()#.convert_alpha() 改一下这里:# 绘制
self.image.fill((0,0,0))
pygame.draw.rect(self.image,(255,255,255),)# 注意看左边0改成了1还有注释这个:# 旋转图像,得到新的图像和图像位置
self.image = pygame.transform.rotate(self.o,self.angle)
#self.rect = self.image.get_rect()
然后把上面保持图像不变也注释掉# 保证图像中心位置不变
#self.rect.centerx = self.orect.centerx
#self.rect.centery = self.orect.centery
跑一遍试试:
发现了吗? 只有rect.x和rect.y是不变的,但我们希望的是旋转中心不变对吧~
ok此页已结束,请看回到目录点击后面一页{:10_264:}
教程2:移动这个长方体
好啦,我们来看看如何移动这个长方体吧~
首先,在__init__()里面加上四个属性:
self.speedall = 10 # ==== xy轴的总速度
self.speed = # ==== x,y速度的列表
self.w = False # ====w是否按下
self.s = False # ====s是否按下
然后,在update()方法里,增加:
if self.w or self.s: # 计算xy方向的移动速度===
self.speedx = -math.sin(self.radian)*10
self.speedy = -math.cos(self.radian)*10
if self.w:# 前进===
self.orect.centerx += self.speedx
self.orect.centery += self.speedy
elif self.s:# 后退===
self.orect.centerx -= self.speedx
self.orect.centery -= self.speedy
接下来,你就可以运行这些代码了!
你会发现,我们的目标好像已经完成了!
当然啦~还有很多问题:
正常的车辆在不移动时是不可以转向的(能转的是坦克吧~)并且,车辆的旋转速度是与车辆行驶速度挂钩的(对比左上和右上图) (右上)
正确的车辆在方向盘不动的情况下,向前和向后的旋转方向应该相反(向前的时候是顺时针向后就应该逆时针)(左上图中我一直按着向左旋转,并且前后移动,一直在逆时针旋转)(右上图是正确的)
还有一个小细节——车辆的旋转中心并不是真正的在它的中心(几何重心)不知道有没有鱼油发现呢~{:10_305:}
上面的问题先留个悬念,下一节在讲
好啦,先看看完整的代码吧~import pygame
import sys
import math
from pygame.locals import *
class Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
self.image = pygame.Surface().convert_alpha()
self.o = self.image # 备份原图 :看过小甲鱼教程的都知道,旋转会破坏图片
self.orect = self.o.get_rect() #备份原图位置:图片旋转后位置会发生变化
self.rect = self.orect
# 设置位置
self.orect.centerx = 400
self.orect.centery = 300
# 绘制
pygame.draw.rect(self.image,(255,255,255),)
self.speedall = 10 # ==== xy轴的总速度
self.speed = # ==== x,y速度的列表
self.angle = 0
self.turn = 0 # 为了方便,在主循环里改
self.w = False # ====w是否按下
self.s = False # ====s是否按下
def update(self):
# 转动
self.angle += self.turn
# 防止角度>360°,方便后面的计算
if self.angle > 360:
self.angle -= 360
# 防止角度<0°, 方便后面的计算
if self.angle < 0:
self.angle += 360
# 得到角度的弧度
self.radian = math.pi/180*self.angle
# 旋转图像,得到新的图像和图像位置
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
# 保证图像中心位置不变
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
if self.w or self.s: # 计算xy方向的移动速度===
self.speedx = -math.sin(self.radian)*10
self.speedy = -math.cos(self.radian)*10
if self.w:# 前进===
self.orect.centerx += self.speedx
self.orect.centery += self.speedy
elif self.s:# 后退===
self.orect.centerx -= self.speedx
self.orect.centery -= self.speedy
if __name__ == "__main__":
size = width, height = 800,600
screen = pygame.display.set_mode(size)
pygame.display.set_caption("title")
clock = pygame.time.Clock()
delay = 60 # 延时计时器
time = 0
# 背景颜色
bg = (85,85,85)
# 是否全屏
fullscreen = False
screen_change = False
running = True
#实例化对象
sprites = pygame.sprite.Group()
car = Car()
sprites.add(car)
while running:
clock.tick(60)
# 检测是否全屏
if fullscreen and screen_change:
screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
screen_change = False
elif screen_change:
screen = pygame.display.set_mode(size)
screen_change = False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print("左键按下")
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
#F11切换全屏
elif event.key == K_F11:
fullscreen = not fullscreen
screen_change = True
elif event.key == K_a:
car.turn = 3
elif event.key == K_d:
car.turn = -3
elif event.key == K_w:
car.w = True
elif event.key == K_s:
car.s = True
elif event.type == KEYUP:
if event.key == K_a:
if car.turn >0:
car.turn = 0
elif event.key == K_d:
if car.turn <0:
car.turn = 0
elif event.key == K_w:
car.w = False
elif event.key == K_s:
car.s = False
#画背景
screen.fill(bg)
#画 xxxx
sprites.draw(screen)
sprites.update()
# 刷新界面
pygame.display.update()
各位鱼油可以自己跑一跑程序,体会一下
整理代码
当然啦,在下一页之前,先把代码整理一下,顺便避免在不移动时出现的转向操作,要更改的地方都用“+++”,“---”标出来了
class Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
self.image = pygame.Surface().convert_alpha() # ===
self.o = self.image # 备份原图 :看过小甲鱼教程的都知道,旋转会破坏图片
self.orect = self.o.get_rect() #备份原图位置:图片旋转后位置会发生变化
self.rect = self.orect
# 设置位置
self.orect.centerx = 400
self.orect.centery = 300
# 绘制
self.image.fill((0,0,0,0))
pygame.draw.rect(self.image,(255,255,255),)
# 画四个轮子
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
self.total_speed = 10 # xy轴的总速度
self.speed = # x,y速度的列表
self.angle = 0
self.turn = 0 # 为了方便,在主循环里改
self.w = False # w是否按下
self.s = False # 是否按下
def update(self):
# 转动----
# 防止角度>360°,方便后面的计算
if self.angle > 360:
self.angle -= 360
# 防止角度<0°, 方便后面的计算
if self.angle < 0:
self.angle += 360
# 得到角度的弧度------
# 旋转图像,得到新的图像和图像位置----
# 保证图像中心位置不变-----
# 计算xy方向的移动速度-----
if self.w:# 前进
self.angle += self.turn # 计算角度+++
self.radius = math.pi/180*self.angle # 转化弧度+++
#旋转图像,得到新的图像和图像位置+++
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
# 保证图像中心位置不变+++
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
# 计算xy方向的移动速度+++
self.speed = -math.sin(self.radius)*self.total_speed
self.speed = -math.cos(self.radius)*self.total_speed
# 移动图像
self.orect.centerx += self.speed # 这里把speedx改成speed
self.orect.centery += self.speed # 这里把speedx改成speed
elif self.s:# 后退
self.angle -= self.turn # 计算角度+++
self.radius = math.pi/180*self.angle # 转化弧度+++
#旋转图像,得到新的图像和图像位置+++
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
# 保证图像中心位置不变+++
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
# 计算xy方向的移动速度+++
self.speed = -math.sin(self.radius)*self.total_speed
self.speed = -math.cos(self.radius)*self.total_speed
self.orect.centerx -= self.speed # 这里把speedx改成speed
self.orect.centery -= self.speed # 这里把speedx改成speed
print(self.angle)
这样在刷新的时候就可以避免在不移动时的转向操作了!
教程3:平滑的手感是如何实现的
其实这一节应该会很简单,
只要多设置几个转向变量和移动变量就可以啦!
简单来说就是:
转向加速,当前帧转向速度,转向最大速度
移动加速度,当前移动速度,移动最大速度
怎么样!是不是已经很清晰啦!{:10_279:}
self.all_speed = 0 # 当前帧x速度+y速度
self.max_speed = 30 # 最大速度
self.speed = #x,y速度
self.speed_up = 0.2 # 每帧增加速度
self.total_speed = 0 # x,y方向的总速度加速减速的时候更改total_speed就可以啦~speed的值每帧都会通过total_speed重新计算
self.angle = 0 # 现在的角度
self.turn = 0 # 这帧旋转速度(随速度增加)
self.turn_speed = 0.2 # 转向加速度
self.max_turn = max_turn # 最大时速时最大旋转速度
(大家不要学我瞎给变量取名,如果不会起名字还不如用中文,都是血和泪的教训)
当然啦,要像车子平滑的减速,必须要有阻力,而不是一松油门直接停车
self.resist = 0.1 # 无操作时每帧减速(阻力)
这时候,我们判断车辆前进还是后退就不可以继续使用W 、S是否按下了,
那么要如何减速呢? 给你0.5秒思考一下!{:10_256:}
嘿嘿嘿聪明的鱼油们一定想到了吧!没错!判断当前帧总移动速度->total_speed是否大于阻力就可以啦~
小提示1:# 判断油门刹车是否踩下和是否达到最高速度
if self.w and self.total_speed < self.max_speed:
self.total_speed += self.speed_up
elif self.s and self.total_speed > -self.max_speed/2:
self.total_speed -= self.speed_up
小提示2:# 如果向前移动
if self.total_speed > self.resist+0.01: # 0.01是为了防止闪烁
# 模拟方向盘自动回正(车头转动速度平滑归零)
if not self.d and self.turn < 0:# 转向速度每帧降低
self.turn += self.turn_speed
if self.turn >= -self.turn_speed+0.01: # 保证转向角度成功归零(没有小数)
self.turn = 0
elif not self.a and self.turn > 0:
self.turn -= self.turn_speed
if self.turn <= self.turn_speed-0.01: # 保证转向角度成功归零(没有小数)
self.turn = 0
{:10_319:}
接下来,加上一个简简单单的背景类:class Thing(pygame.sprite.Sprite):
def __init__(self):
# 背景
super().__init__()
self.width,self.height = randint(10,300),randint(10,300)
self.image = pygame.Surface().convert_alpha()
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = randint(-800,1600),randint(-800,1600)
pygame.draw.rect(self.image,
(randint(100,255),randint(100,255),randint(100,255)),
)
使摄像机跟随车辆移动————车辆始终保持在屏幕中心,移动其他对象。
更多关于摄像机移动的教程可以先看看隔壁 @歌者文明清理员的贴子:[作品展示] 【歌者-Pygame】pygame 天体运动模拟(附教程)的 鼠标event 教程
来看看这一小节的成果吧~
注意: 下方代码仅供参考及体验,更优秀的代码等着各位鱼油来写我总是把代码直接给你们,这可不行,各位鱼油一定要有自己写代码的能力!
因为下面的代码是楼主在研究车辆移动的时候写的,变量名可能和上面提到的有所出入,各位鱼油在自己写的时候请以自己电脑的为准!
绝对不是我懒的再把代码重新优化好发上来。。。{:10_297:}
看看代码吧~
import pygame
import sys
import math
from random import randint
from pygame.locals import *
class Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
self.image = pygame.Surface().convert_alpha()
self.o = self.image # 备份原图
self.orect = self.o.get_rect()
self.rect = self.orect
# 设定初始位置
self.orect.centerx = 400
self.orect.centery = 300
# 透明背景和主题
pygame.draw.rect(self.image,(0,0,0,0),)
pygame.draw.rect(self.image,(255,255,255),)
# 画四个轮子
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
self.all_speed = 0 # 当前帧x速度+y速度
self.max_speed = 30
self.speed =
self.speed_up = 0.2 # 每帧增加速度
self.angle = 0
self.turn = 5 # 最大时速时旋转速度
self.resist = 0.1 # 无操作时每帧减速(阻力)
self.w = False
self.s = False
self.forward = False
def update(self):
# 保证角度不会超过360
if self.angle >= 360:
self.angle -= 360
# 保证角度不会是负数,为后面的计算提供方便
if self.angle < 0:
self.angle = 360+self.angle
self.radius = math.pi/180*self.angle # 转化为弧度
# 旋转车
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
# 判断油门刹车是否踩下和是否达到最高速度
if self.w and self.all_speed < self.max_speed:
self.all_speed += self.speed_up
elif self.s and self.all_speed > -self.max_speed/2:
self.all_speed -= self.speed_up
# 计算移动
if self.all_speed > 0:
self.speed = -math.sin(self.radius)*self.all_speed
self.speed = -math.cos(self.radius)*self.all_speed
if self.all_speed > self.resist:
self.angle += self.turn*(self.all_speed/self.max_speed)
self.all_speed -= self.resist
elif self.all_speed < 0:
self.speed = -math.sin(self.radius)*self.all_speed
self.speed = -math.cos(self.radius)*self.all_speed
if self.all_speed < self.resist:
self.angle += self.turn*(self.all_speed/self.max_speed)
self.all_speed += self.resist
class Thing(pygame.sprite.Sprite):
def __init__(self):
# 背景
super().__init__()
self.width,self.height = randint(10,300),randint(10,300)
self.image = pygame.Surface().convert_alpha()
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = randint(-800,1600),randint(-800,1600)
pygame.draw.rect(self.image,
(randint(100,255),randint(100,255),randint(100,255)),
)
if __name__ == "__main__":
size = width, height = 800,600
screen = pygame.display.set_mode(size)
pygame.display.set_caption("title")
clock = pygame.time.Clock()
delay = 60 # 延时计时器
time = 0
# 背景颜色
bg = (85,85,85)
# 是否全屏
fullscreen = False
screen_change = False
running = True
#实例化对象
sprites = pygame.sprite.Group()
bgthings = pygame.sprite.Group()
car = Car()
# 生成背景
for i in range(300):
#sprites.add(Thing())
bgthings.add(Thing())
sprites.add(car)
while running:
clock.tick(60)
# 检测是否全屏
if fullscreen and screen_change:
screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
screen_change = False
elif screen_change:
screen = pygame.display.set_mode(size)
screen_change = False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print("左键按下")
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
#F11切换全屏
elif event.key == K_F11:
fullscreen = not fullscreen
screen_change = True
elif event.key == K_a:
car.turn = 5
elif event.key == K_d:
car.turn = -5
elif event.key == K_w:
car.w = True
elif event.key == K_s:
car.s = True
elif event.key == K_q:
car.q = True
elif event.key == K_e:
car.e = True
elif event.type == KEYUP:
if event.key == K_a:
if car.turn >0:
car.turn = 0
elif event.key == K_d:
if car.turn <0:
car.turn = 0
elif event.key == K_w:
car.w = False
elif event.key == K_s:
car.s = False
elif event.key == K_q:
car.q = False
elif event.key == K_e:
car.e = False
# 移动镜头
for bgthing in bgthings:
bgthing.rect.x -= car.speed
bgthing.rect.y -= car.speed
#画背景
screen.fill(bg)
#画 xxxx
sprites.update()
bgthings.update()
bgthings.draw(screen)
sprites.draw(screen)
# 刷新界面
pygame.display.update()
漂移的实现理论
这个看起来很牛*,但是实现起来是很简单的。{:10_256:}
这里我用的方法是把移动方向的角度和车体的角度分开计算,使漂移时的移动角度比车体角度大一点就可以啦~
当然啦,在此之前,还要修正一个bug,不知道有没有聪明的鱼油发现呢~
当车辆移动速度非常低的时候,操作手感非常差,(把最高速度改成<5的就可以感受到啦)
(注意看车辆的拖尾,一直在摇晃,这个bug现在还不太明显,但是如果车做的很小就无法忽略它的影响了)
这是因为pygame的移动最小单位是1像素,四舍五入,如果移动速度小于1就会出现问题
(鱼油要自己思考一下再看上面的答案哦)
没错,所以我们就要自己搓一个计算小数的移动算法啦!
代码如下:# 计算移动
self.speed = -math.sin(self.move_radius)*self.total_speed
self.speed = -math.cos(self.move_radius)*self.total_speed
# 计算移速小数
self.small_speed += self.speed - int(self.speed)
self.speed = int(self.speed)
self.small_speed += self.speed - int(self.speed)
self.speed = int(self.speed)
# 计算移动(计算小数)
if self.small_speed >1:
self.speed += 1
self.small_speed -= 1
elif self.small_speed < -1:
self.speed -= 1
self.small_speed += 1
if self.small_speed >1:
self.speed += 1
self.small_speed -= 1
elif self.small_speed < -1:
self.speed -= 1
self.small_speed += 1
然后来看看漂移的算法:#计算角度
if self.space and ((self.a and self.move_angle < self.angle+0.1) or (self.d and self.move_angle > self.angle-0.1)):
# 如果空格按下,并且车头的角度大于移动的角度 开始主动漂移
self.move_angle += self.turn*(self.total_speed/self.max_speed)*1.1
self.angle += self.turn*(self.total_speed/self.max_speed)*1.5
else:# 不漂移时,车头转向速度与移动方向转向速度相同
# 如果没有主动漂移
self.angle += self.turn*(self.total_speed/self.max_speed)
# 移动方向自动向车头方向靠拢 打滑
if self.move_angle > self.angle+0.1:
self.move_angle-= self.turn_restore
elif self.move_angle < self.angle-0.1:
self.move_angle+= self.turn_restore
# 前进方向和车头方向夹角大于100,锁定角度
if self.move_angle - self.angle > self.esp:
self.move_angle = self.angle + self.esp
elif self.angle - self.move_angle > self.esp:
self.move_angle = self.angle - self.esp
#阻力
self.total_speed -= self.resist(这是车辆前进时的,后退时有一些量要改成负的)
各位鱼油有看不懂的地方及时提问哦!我会把缺少的再更新上来的!毕竟我的注释基本上每行都写了所以就 不再细讲代码了
好啦!看看完整代码吧:
import pygame
import sys
import math
from random import randint
from pygame.locals import *
class Trace(pygame.sprite.Sprite):
def __init__(self,color=(255,255,255,255),position=,radius=0,time=-1):
super().__init__()
self.position = position
self.image = pygame.Surface((radius*2,radius*2), pygame.SRCALPHA).convert_alpha()
self.rect = self.image.get_rect(center=position)
self.color = color
self.radius = radius
self.time = time
self.total_time = time
self.alpha = self.color
self.total_alpha = self.color
pygame.draw.circle(
self.image,
color=self.color,
center=(self.radius,self.radius),
radius=self.radius
)
def update(self):
self.time -= 1
self.alpha = self.total_alpha*(self.time/self.total_time)
self.image.set_alpha(self.alpha)
class Car(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.width,self.height = 50,100
self.image = pygame.Surface(, pygame.SRCALPHA)#pygame.SRCALPHA 填充透明背景
self.o = self.image # 备份原图
self.orect = self.o.get_rect()
self.rect = self.orect
# 设定初始位置
self.orect.centerx = 400
self.orect.centery = 300
# 主体
pygame.draw.rect(self.image,(255,255,255),)
# 画四个轮子
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
pygame.draw.rect(self.image,(0,0,0),)
self.total_speed = 0 # 当前帧x速度+y速度
self.small_speed = # 存放移速零头
self.max_speed = 10
self.speed =
self.speed_up = 0.2 # 每帧增加速度
self.angle = 0 # 先在的角度
self.turn = 0 # 这帧旋转速度
self.move_angle = 0 # 移动方向的角度
self.max_turn = 3 # 最大时速时最大旋转速度
self.turn_speed = 0.2 # 转向加速度
self.resist = 0.1 # 无操作时每帧减速(阻力)
self.gears = [(10,1),(20,0.2),(30,0.12)] # [(最大时速,加速度)]
self.radius = 0
self.control_ability = 0.9 # 操纵性能(打滑、漂移回正速度)
self.turn_restore = self.max_turn*self.control_ability # 移动速度的方向改变速度
self.esp = 100 # 最大漂移角度
# 按键状态
self.w = False
self.s = False
self.a = False
self.d = False
self.space=False
# 车辆状态
self.forward = False
def update(self):
# 保证角度不会超过360
if self.angle >= 360 and self.move_angle >= 360:
self.angle -= 360
self.move_angle -= 360
# 保证角度不会是负数,为后面的计算提供方便
if self.angle < 0 and self.move_angle < 0:
self.angle = 360+self.angle
self.move_angle = 360+self.move_angle
self.radius = math.pi/180*self.angle # 转化为弧度
self.move_radius = math.pi/180*self.move_angle
# 旋转车
self.image = pygame.transform.rotate(self.o,self.angle)
self.rect = self.image.get_rect()
self.rect.centerx = self.orect.centerx
self.rect.centery = self.orect.centery
# 判断油门刹车是否踩下和是否达到最高速度
if self.w and self.total_speed < self.max_speed:
self.total_speed += self.speed_up
elif self.s and self.total_speed > -self.max_speed/2:
self.total_speed -= self.speed_up
# 计算移动
self.speed = -math.sin(self.move_radius)*self.total_speed
self.speed = -math.cos(self.move_radius)*self.total_speed
# 计算移速小数
self.small_speed += self.speed - int(self.speed)
self.speed = int(self.speed)
self.small_speed += self.speed - int(self.speed)
self.speed = int(self.speed)
# 计算移动(计算小数)
if self.small_speed >1:
self.speed += 1
self.small_speed -= 1
elif self.small_speed < -1:
self.speed -= 1
self.small_speed += 1
if self.small_speed >1:
self.speed += 1
self.small_speed -= 1
elif self.small_speed < -1:
self.speed -= 1
self.small_speed += 1
# 转向 模拟方向盘转动过程
if self.d and self.turn > -self.max_turn:# 转向速度每帧提高
self.turn -= self.turn_speed
elif self.a and self.turn < self.max_turn:
self.turn += self.turn_speed
# 如果向前移动
if self.total_speed > self.resist+0.01: # 0.01是为了防止闪烁
# 模拟方向盘自动回正(车头转动速度平滑归零)
if not self.d and self.turn < 0:# 转向速度每帧降低
self.turn += self.turn_speed
if self.turn >= -self.turn_speed+0.01: # 保证转向角度成功归零(没有小数)
self.turn = 0
elif not self.a and self.turn > 0:
self.turn -= self.turn_speed
if self.turn <= self.turn_speed-0.01: # 保证转向角度成功归零(没有小数)
self.turn = 0
#计算角度
if self.space and ((self.a and self.move_angle < self.angle+0.1) or (self.d and self.move_angle > self.angle-0.1)):
# 如果空格按下,并且车头的角度大于移动的角度 开始主动漂移
self.move_angle += self.turn*(self.total_speed/self.max_speed)*1.1
self.angle += self.turn*(self.total_speed/self.max_speed)*1.5
else:# 不漂移时,车头转向速度与移动方向转向速度相同
# 如果没有主动漂移
self.angle += self.turn*(self.total_speed/self.max_speed)
# 移动方向自动向车头方向靠拢 打滑
if self.move_angle > self.angle+0.1:
self.move_angle-= self.turn_restore
elif self.move_angle < self.angle-0.1:
self.move_angle+= self.turn_restore
# 前进方向和车头方向夹角大于100,锁定角度
if self.move_angle - self.angle > self.esp:
self.move_angle = self.angle + self.esp
elif self.angle - self.move_angle > self.esp:
self.move_angle = self.angle - self.esp
#阻力
self.total_speed -= self.resist
# 如果倒车
elif self.total_speed < -self.resist-0.01:
# 模拟方向盘自动回正 (平滑归零)
if not self.d and self.turn < 0:# 转向速度每帧降低
self.turn += self.turn_speed
if self.turn >= -self.turn_speed-0.01:# 保证转向角度成功归零(没有小数)
self.turn = 0
elif not self.a and self.turn > 0:
self.turn -= self.turn_speed
if self.turn <= self.turn_speed+0.01:# 保证转向角度成功归零(没有小数)
self.turn = 0
#计算角度
if self.space and ((self.a and self.move_angle > self.angle+0.1) or (self.d and self.move_angle < self.angle-0.1)):
# 漂移
self.move_angle += self.turn*(self.total_speed/self.max_speed)*1.1
self.angle += self.turn*(self.total_speed/self.max_speed)*1.5
else:# 不漂移时,车头转向速度与移动方向转向速度相同
self.angle += self.turn*(self.total_speed/self.max_speed)
# 移动方向自动向车头方向靠拢 打滑
if self.move_angle > self.angle-0.01:
self.move_angle -= self.turn_restore
elif self.move_angle < self.angle+0.01:
self.move_angle += self.turn_restore
# 后退方向和车头方向夹角大于100,锁定角度
if self.move_angle - self.angle > self.esp/2:
self.move_angle = self.angle + self.esp/2
elif self.angle - self.move_angle > self.esp/2:
self.move_angle = self.angle - self.esp/2
#阻力
self.total_speed += self.resist
def create_trace(self):
def count(position):
r=((position-self.rect.centerx)**2 + (position-self.rect.centery)**2)**0.5
if position > self.rect.centerx:
sin = (position-self.rect.centery)/r
radian = math.asin(sin)
radian -= self.radius
x = self.rect.centerx + r*math.cos(radian)
y = self.rect.centery + r*math.sin(radian)
elif position < self.rect.centerx:
sin = -(position-self.rect.centery)/r
radian = math.asin(sin)
radian -= self.radius
x = self.rect.centerx - r*math.cos(radian)
y = self.rect.centery - r*math.sin(radian)
return
trace1 = Trace(color=,
position=count(),
radius=5,
time=10)
trace2 = Trace(color=,
position=count(),
radius=5,
time=10)
trace3 = Trace(color=,
position=count(),
radius=5,
time=10)
trace4 = Trace(color=,
position=count(),
radius=5,
time=10)
return
class Thing(pygame.sprite.Sprite):
def __init__(self):
# 背景
super().__init__()
self.time = -1
self.width,self.height = randint(10,300),randint(10,300)
self.image = pygame.Surface().convert_alpha()
self.rect = self.image.get_rect()
self.rect.x, self.rect.y = randint(-800,1600),randint(-800,1600)
pygame.draw.rect(self.image,
(randint(100,255),randint(100,255),randint(100,255)),
)
if __name__ == "__main__":
size = width, height = 800,600
screen = pygame.display.set_mode(size)
pygame.display.set_caption("漂移")
clock = pygame.time.Clock()
delay = 60 # 延时计时器
time = 0
# 背景颜色
bg = (85,85,85)
# 是否全屏
fullscreen = False
screen_change = False
running = True
#实例化对象
sprites = pygame.sprite.Group()
bgthings = pygame.sprite.Group()
car = Car()
# 生成背景
for i in range(300):
#sprites.add(Thing())
bgthings.add(Thing())
sprites.add(car)
while running:
clock.tick(60)
# 检测是否全屏
if fullscreen and screen_change:
screen = pygame.display.set_mode(size,FULLSCREEN,HWSURFACE)
screen_change = False
elif screen_change:
screen = pygame.display.set_mode(size)
screen_change = False
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print("左键按下")
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
#F11切换全屏
elif event.key == K_F11:
fullscreen = not fullscreen
screen_change = True
elif event.key == K_a:
car.a = True
elif event.key == K_d:
car.d = True
elif event.key == K_w:
car.w = True
elif event.key == K_s:
car.s = True
elif event.key == K_q:
car.q = True
elif event.key == K_e:
car.e = True
elif event.key == K_1:
car.max_speed = car.gears
car.speed_up = car.gears
elif event.key == K_2:
car.max_speed = car.gears
car.speed_up = car.gears
elif event.key == K_3:
car.max_speed = car.gears
car.speed_up = car.gears
elif event.key == K_SPACE:
car.space=True
elif event.type == KEYUP:
if event.key == K_a:
car.a = False
#if car.turn >0:
#car.turn = 0
elif event.key == K_d:
car.d = False
#if car.turn <0:
#car.turn = 0
elif event.key == K_w:
car.w = False
elif event.key == K_s:
car.s = False
elif event.key == K_q:
car.q = False
elif event.key == K_e:
car.e = False
elif event.key == K_SPACE:
car.space=False
# 生成轮胎印记
for trace in car.create_trace():
bgthings.add(trace)
for trace in bgthings:
if trace.time == 0:
bgthings.remove(trace)
# 移动镜头
for bgthing in bgthings:
bgthing.rect.x -= car.speed
bgthing.rect.y -= car.speed
#画背景
screen.fill(bg)
#画 xxxx
sprites.update()
bgthings.update()
bgthings.draw(screen)
sprites.draw(screen)
# 刷新界面
pygame.display.update()
学会了吗!评论区见哦~
https://fishc.com.cn/static/image/hrline/line7.png这里可能要先跟大家伙道个歉,模拟车辆移动现在还不成熟,所以更新可能会很慢,但是一定会努力给大家带来更高质量的帖子https://fishc.com.cn/static/image/hrline/line7.png
更新中。。。。这个帖子车不多完结了,但以后还是会看情况更新的 感觉这教程写的一塌糊涂啊
评分!!!评分!!!评分!!!
求评分!求千斤顶!求制定卡!求帮忙@!求推荐!求评论!
感谢
[*]首先感谢各位支持我的鱼油,是你们的支持让我一直走到今天
[*]感谢各位版主,管理员和不二老师
[*]感谢@歌者文明清理员 ,@python爱好者. 等同志帮我做的宣传和提供的帮助帮我修复了 · bug 虽然最后我自己解决了bug
[*]感谢小甲鱼老师和闪耀捕捉器鱼C闪耀屏幕捕捉器为我制作帖子提供了很大很大的方便
@Mike_python小 @中英文泡椒 @学习编程中的Ben @sfqxx @python爱好者. @学习学习在研究 @zhangjinxuan @liuhongrun2022 激动人心,无以言表,难得的好帖啊
期待后面的更新 python爱好者. 发表于 2023-7-23 21:02
激动人心,无以言表,难得的好帖啊
期待后面的更新
谢谢支持,我一定不会辜负你的期望{:10_297:}
以后一定会努力学习,多学知识跟大家分享,带来跟多优秀的帖子!{:10_257:} 我草,这么好的帖子,今天没有评分了,明天必须补上!!! Mike_python小 发表于 2023-7-23 21:05
我草,这么好的帖子,今天没有评分了,明天必须补上!!!
谢谢谢谢 不是,这么牛逼,上一次见到这么好的帖子还是Twilight6 大神的杰作 支持! Mike_python小 发表于 2023-7-23 21:07
不是,这么牛逼,上一次见到这么好的帖子还是Twilight6 大神的杰作
没有没有,我的水平是远远比不上那位大佬的,大牛排行榜第一可是我们全论坛的神{:10_328:}
不过我会努力向他学习的 cjjJasonchen 发表于 2023-7-23 21:04
谢谢支持,我一定不会辜负你的期望
以后一定会努力学习,多学知识跟大家分享,带来跟多优秀 ...
@不二如是 @isdkz @歌者文明清理员 @sfqxx @Twilight6 @liuzhengyuan @人造人 @学习编程中的Ben @小伤口 @tommyyu 支持一下,这是个人才 python爱好者. 发表于 2023-7-23 21:11
@不二如是 @isdkz @歌者文明清理员 @sfqxx @Twilight6 @liuzhengyuan @人造人 @学习编程中的Ben @小伤口...
真的是非常感谢你,帮我@{:10_278:}
其实甲鱼老师和不二老师我像跟新完再@的
太厉害了{:10_257:} wc python爱好者. 发表于 2023-7-23 21:11
@不二如是 @isdkz @歌者文明清理员 @sfqxx @Twilight6 @liuzhengyuan @人造人 @学习编程中的Ben @小伤口...
已经支持 python爱好者. 发表于 2023-7-23 21:11
@不二如是 @isdkz @歌者文明清理员 @sfqxx @Twilight6 @liuzhengyuan @人造人 @学习编程中的Ben @小伤口...
是的
@小甲鱼 @青出于蓝 @liuhongrun2022 @一点沙 @陈尚涵 太厉害了 Mike_python小 发表于 2023-7-23 22:15
太厉害了
{:10_278:} 太厉害了 太厉害了 Mike_python小 发表于 2023-7-23 22:19
太厉害了
谢谢谢谢{:10_297:}