配套视频教程
本节最终效果:
一个游戏大致可以分为以下几个标准处理过程:
init() 初始化
new() 游戏初次进入(或主角挂了,重新开始时)
update() 游戏逻辑更新
events() 事件处理(响应键盘、鼠标等事件)
draw() 屏幕渲染绘制
show_start_screen() 游戏的启动画面
show_go_screen() 游戏结束时的画面
run() 游戏运行的循环入口
我们将上节的游戏模板,实现成一个Game类:
import pygame as pg import random from settings import * from sprites import * class Game: def __init__(self): # initialize game window, etc pg.init() pg.mixer.init() self.screen = pg.display.set_mode((WIDTH, HEIGHT)) pg.display.set_caption(TITLE) self.clock = pg.time.Clock() self.running = True def new(self): # start a new game self.all_sprites = pg.sprite.Group() self.player = Player() self.all_sprites.add(self.player) self.run() def run(self): # Game Loop self.playing = True while self.playing: self.clock.tick(FPS) self.events() self.update() self.draw() # 为了方便观察pos,vel,acc这些变量,定义一个debug辅助函数 def debug(self): font = pg.font.SysFont('Menlo', 25, True) pos_txt = font.render( 'Pos:(' + str(round(self.player.pos.x, 2)) + "," + str(round(self.player.pos.y, 2)) + ")", 1, GREEN) vel_txt = font.render( 'Vel:(' + str(round(self.player.vel.x, 2)) + "," + str(round(self.player.vel.y, 2)) + ")", 1, GREEN) acc_txt = font.render( 'Acc:(' + str(round(self.player.acc.x, 2)) + "," + str(round(self.player.acc.y, 2)) + ")", 1, GREEN) self.screen.blit(pos_txt, (20, 10)) self.screen.blit(vel_txt, (20, 40)) self.screen.blit(acc_txt, (20, 70)) def update(self): # Game Loop - Update self.all_sprites.update() def events(self): # Game Loop - events for event in pg.event.get(): # check for closing window if event.type == pg.QUIT: if self.playing: self.playing = False self.running = False def draw(self): # Game Loop - draw self.screen.fill(BLACK) self.all_sprites.draw(self.screen) # *after* drawing everything, flip the display self.debug() pg.display.flip() def show_start_screen(self): # game splash/start screen pass def show_go_screen(self): # game over/continue pass g = Game() g.show_start_screen() while g.running: g.new() g.show_go_screen() pg.quit()
注:有2个控制变量,running是控制pygame是否退出,而playing是游戏情节是否继续处理
(即:有可能游戏情况结束,比如:主角挂了,显示game over,但是pygame并不需要退出,可以选择重新开始)
settings.py
TITLE = "Rabbit jump on the sky!" WIDTH = 480 HEIGHT = 600 FPS = 60 # Player properties PLAYER_ACC = 0.5 PLAYER_FRICTION = -0.12 # define colors WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 255, 0)
定义游戏的精灵(下面代码命名为sprites.py)
# Sprite classes for platform game import pygame as pg from settings import * vec = pg.math.Vector2 class Player(pg.sprite.Sprite): def __init__(self): pg.sprite.Sprite.__init__(self) self.image = pg.Surface((30, 40)) self.image.fill(YELLOW) self.rect = self.image.get_rect() self.rect.center = (WIDTH / 2, HEIGHT / 2) self.pos = vec(WIDTH / 2, HEIGHT / 2) self.vel = vec(0, 0) self.acc = vec(0, 0) def update(self): self.acc = vec(0, 0) keys = pg.key.get_pressed() if keys[pg.K_LEFT]: self.acc.x = -PLAYER_ACC if keys[pg.K_RIGHT]: self.acc.x = PLAYER_ACC # apply friction self.acc += self.vel * PLAYER_FRICTION # equations of motion self.vel += self.acc self.pos += self.vel + 0.5 * self.acc # wrap around the sides of the screen if self.pos.x > WIDTH: self.pos.x = 0 if self.pos.x < 0: self.pos.x = WIDTH self.rect.center = self.pos
- 在2D游戏中,会大量用到类似(x,y)的结构,pygame中已经把这种结构封装成了Vector2.
- 加速度的定义:单位时间内速度的变化量。在游戏中,单位时间就是每帧,所以每一帧,我们在速度self.vel(velocity的缩写)值上的改变即为加速度self.acc( acceleration的缩写),因为
v=a*t
这里t可以理解为1.
3.计算player新位移
self.pos += self.vel + 0.5 * self.acc
使用位移公式(t同样理解为1),1帧
s = v*t+1/2*a*t**2
- 摩擦力的效果
PLAYER_FRICTION = -0.12 self.acc += self.vel * PLAYER_FRICTION self.vel += self.acc
等价于
self.acc = self.acc + self.vel * PLAYER_FRICTION self.vel = self.vel + self.acc
也即:
PLAYER_FRICTION = -0.12 self.vel = self.vel + self.vel * PLAYER_FRICTION
摩擦力的效果,表现为阻碍物体运动,具体在代码中体现,只要想办法把速度减少一点点。以上代码实现了该效果。
可以通过调试观察到,方块向右,速度由0变正,再由于摩擦力的作用变为0;反之相反。方块向右,加速度由正及负最后变为0,反之相反。速度为正,方块位置增加(表现为向右移动);速度为负,方块位置减小(表现为向左移动);方块向右,加速度为正,使得速度变大(正的大),方块向左,加速度为负,使得速度变大(负的大,实际上是变小)