python - Weird platform movement in pygame side-scrolling platformer
问题描述
So I've been making this platformer game for a school project, but I've run into a major issue. Whenever my character moves, my platforms (which are made of 32x32 blocks) don't move at the same speed on the x axis and end up clipping into each other until the level and the platforms become tiny.
I first thought that the friction I put on my character might have been the problem as it caused bugs with collision, but after I made it much smoother the problem stayed the same. The weird thing is that all of my platforms will clip into each other except the last one, which doesn't really make sense to me.
Here's the code I'm using to move the platforms so that they give this moving camera effect. The +1s in the bottom half of each half are used to make sure the character can use the latent friction from his movement to slide all the way to the edge of the screen:
if coll:
self.joueur.vel.y*=-0.2
if self.joueur.rect.right>=width-350 and self.joueur.rect.right<width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)
if self.joueur.rect.right>=width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)
if self.joueur.rect.left<=width-450 and self.joueur.rect.left>width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)
if self.joueur.rect.left<=width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)+1
Here's the full code from my main.py file:
import random
import os
from settings import*
vec=pygame.math.Vector2
gamefile=os.path.dirname(__file__)
imagefile=os.path.join(gamefile,"sprites")
passed_time=0
class Joueur(pygame.sprite.Sprite):
def __init__(self,jeu):
pygame.sprite.Sprite.__init__(self)
self.jeu=jeu
self.bloup=False
self.image=pygame.image.load(os.path.join(imagefile,"32character_1.png"))
self.image.set_colorkey(BLACK)
self.rect=self.image.get_rect()
self.rect.center=(width/2,height/2)
self.pos=vec(width/2,700)
self.vel=vec(0,0)
self.acc=vec(0,0)
def saut(self):
self.rect.y+=1
coll=pygame.sprite.spritecollide(self,self.jeu.platform,False)
self.rect.y-=1
if coll:
self.vel.y=-18 #-18
def update(self):
self.acc=vec(0,0.8)
keys=pygame.key.get_pressed()
if keys[pygame.K_a]:
self.image=pygame.image.load(os.path.join(imagefile,"32character_1_flip.png"))
self.image.set_colorkey(BLACK)
self.acc.x=-acc_joueur
if keys[pygame.K_d]:
self.image=pygame.image.load(os.path.join(imagefile,"32character_1.png"))
self.image.set_colorkey(BLACK)
self.acc.x=acc_joueur
self.acc.x+=self.vel.x*frict_joueur
self.acc.y+=self.vel.y*frict_air
self.vel+=self.acc
self.pos+=self.vel+0.5*self.acc
self.rect.midbottom=self.pos
class Grass(pygame.sprite.Sprite):
def __init__(self,x,y,w,h):
pygame.sprite.Sprite.__init__(self)
self.image=self.image=pygame.image.load(os.path.join(imagefile,"grass_block.png"))
self.image.set_colorkey(BLACK)
self.rect=self.image.get_rect()
self.rect.x=x
self.rect.y=y
class Dirt(pygame.sprite.Sprite):
def __init__(self,x,y,w,h):
pygame.sprite.Sprite.__init__(self)
self.image=self.image=pygame.image.load(os.path.join(imagefile,"dirt_block.png"))
self.image.set_colorkey(BLACK)
self.rect=self.image.get_rect()
self.rect.x=x
self.rect.y=y
class Jeu:
def __init__(self):
pygame.init()
pygame.mixer.init()
self.cadre=pygame.display.set_mode((width,height))
pygame.display.set_caption("Hell yeah")
self.clock=pygame.time.Clock()
self.start_time=pygame.time.get_ticks()
self.lives=3
self.death=0
self.running=True
self.font_name=pygame.font.match_font(font_name)
def new(self):
self.allsprites=pygame.sprite.Group()
self.platform=pygame.sprite.Group()
self.joueur=Joueur(self)
self.allsprites.add(self.joueur)
for grass in grass_list:
p=Grass(*grass)
self.allsprites.add(p)
self.platform.add(p)
for dirt in dirt_list:
p=Dirt(*dirt)
self.allsprites.add(p)
self.platform.add(p)
self.run()
def run(self):
self.playing=True
while self.playing:
self.clock.tick(fps)
self.passed_time=pygame.time.get_ticks()-self.start_time
self.events()
self.update()
self.draw()
def update(self):
self.allsprites.update()
coll=pygame.sprite.spritecollide(self.joueur,self.platform,False)
keys=pygame.key.get_pressed()
if self.joueur.vel.y>0:
if coll:
self.joueur.pos.y=coll[0].rect.top+1
self.joueur.vel.y=0
self.joueur.rect.midbottom=self.joueur.pos
if self.joueur.vel.y<0:
if coll:
self.joueur.vel.y*=-0.2
if self.joueur.rect.right>=width-350 and self.joueur.rect.right<width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)
if self.joueur.rect.right>=width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)
if self.joueur.rect.left<=width-450 and self.joueur.rect.left>width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)
if self.joueur.rect.left<=width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)+1
if self.joueur.rect.bottom>height:
pygame.time.wait(500)
self.death+=1
self.lives=3-self.death
self.playing=False
def events(self):
for event in pygame.event.get():
if event.type==pygame.QUIT:
if self.playing:
self.playing=False
self.running=False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_SPACE:
self.joueur.saut()
def draw(self):
self.cadre.fill(SKY_BLUE)
self.allsprites.draw(self.cadre)
self.draw_text(str(round(self.passed_time/1000,1))+" s",22,WHITE,32,15)
if self.lives>1:
self.draw_text(str(self.lives)+" lives",20,WHITE,32,35)
if self.lives==1:
self.draw_text(str(self.lives)+" life",20,ORANGE,32,35)
if self.lives==0:
self.show_go_screen()
pygame.display.update()
def show_start_screen(self):
self.cadre.fill(DARK_BLUE)
self.draw_text("Woodland King's Adventure",50,WHITE,400,200)
self.draw_text("A=Move left, D=Move right, SPACE=Jump",24,LIGHT_YELLOW,400,300)
self.draw_text("Press enter to start!",32,LIGHT_GREEN,400,400)
pygame.display.update()
self.wait_for_start()
def show_go_screen(self):
if not self.running:
return
self.cadre.fill(GRAY)
self.draw_text("GAME OVER",50,DARK_RED,400,400)
self.draw_text("Press ESCAPE to quit",30,YELLOW,400,500)
self.draw_text("Pres R to play again",30,GREEN,400,600)
pygame.display.update()
self.wait_for_end()
def wait_for_start(self):
attente=True
while attente:
self.clock.tick(fps)
for event in pygame.event.get():
if event.type==pygame.QUIT:
attente=False
self.running=False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
attente=False
def wait_for_end(self):
attente=True
while attente:
self.clock.tick(fps)
for event in pygame.event.get():
if event.type==pygame.QUIT:
attente=False
self.running=False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
attente=False
self.running=False
if event.key==pygame.K_r:
self.death=0
self.lives=3
attente=False
self.show_start_screen()
def draw_text(self,text,size,color,x,y):
font=pygame.font.Font(self.font_name,size)
text_surface=font.render(text,True,color)
text_rect=text_surface.get_rect()
text_rect.midtop=(x,y)
self.cadre.blit(text_surface,text_rect)
j=Jeu()
j.show_start_screen()
while j.running:
j.new()
pygame.quit()
And here's the settings.py file so you can understand what the platforms look like:
height=800
fps=60
font_name="arial"
acc_joueur=0.88
frict_joueur=-0.19
frict_air=-0.02
#(pos_x,pos_y,largeur,hauteur)
dirt_list=[(300,731,32,32),(300,762,32,32),(300,793,32,32),(332,731,32,32),(332,762,32,32),
(332,793,32,32),(364,731,32,32),(364,762,32,32),(364,793,32,32),(396,731,32,32),
(396,762,32,32),(396,793,32,32),(428,731,32,32),(268,731,32,32),(268,762,32,32),
(268,793,32,32),(428,762,32,32),(428,793,32,32),#base platform
(652,607,32,32),(684,607,32,32),(716,607,32,32),(748,607,32,32),#lil platform 2
(940,545,32,32),(940,576,32,32),(940,607,32,32),(940,638,32,32),(940,669,32,32),
(940,700,32,32),(940,731,32,32),(940,762,32,32),(940,793,32,32),(972,545,32,32),
(972,576,32,32),(972,607,32,32),(972,638,32,32),(972,669,32,32),(972,700,32,32),
(972,731,32,32),(972,762,32,32),(972,793,32,32),(1004,545,32,32),(1004,576,32,32),
(1004,607,32,32),(1004,638,32,32),(1004,669,32,32),(1004,700,32,32),(1004,731,32,32),
(1004,762,32,32),(1004,793,32,32),(1036,545,32,32),(1036,576,32,32),(1036,607,32,32),
(1036,638,32,32),(1036,669,32,32),(1036,700,32,32),(1036,731,32,32),(1036,762,32,32),
(1036,793,32,32),(1068,545,32,32),(1068,576,32,32),(1068,607,32,32),(1068,638,32,32),
(1068,669,32,32),(1068,700,32,32),(1068,731,32,32),(1068,762,32,32),(1068,793,32,32),
(1100,545,32,32),(1100,576,32,32),(1100,607,32,32),(1100,638,32,32),(1100,669,32,32),
(1100,700,32,32),(1100,731,32,32),(1100,762,32,32),(1100,793,32,32),(1132,545,32,32),
(1132,576,32,32),(1132,607,32,32),(1132,638,32,32),(1132,669,32,32),(1132,700,32,32),
(1132,731,32,32),(1132,762,32,32),(1132,793,32,32),#big platform 1
(1388,762,32,32),(1388,793,32,32),(1420,762,32,32),(1420,793,32,32),(1452,762,32,32),
(1452,793,32,32),(1484,762,32,32),(1484,793,32,32),(1516,762,32,32),(1516,793,32,32),
(1548,762,32,32),(1548,793,32,32),(1580,762,32,32),(1580,793,32,32),(1612,762,32,32),
(1612,793,32,32),(1644,762,32,32),(1644,793,32,32),(1676,762,32,32),(1676,793,32,32),
(1708,762,32,32),(1708,793,32,32),(1740,762,32,32),(1740,793,32,32),(1772,762,32,32),
(1772,793,32,32),(1804,762,32,32),(1804,793,32,32),#big platform 2
]
grass_list=[(300,700,32,32),(332,700,32,32),(364,700,32,32),(396,700,32,32),(428,700,32,32),
(268,700,32,32), #base platform
(492,638,32,32),(524,638,32,32),#lil platform 1
(652,576,32,32),(684,576,32,32),(716,576,32,32),(748,576,32,32),#lil platform 2
(940,514,32,32),(972,514,32,32),(1004,514,32,32),(1036,514,32,32),(1068,514,32,32),
(1100,514,32,32),(1132,514,32,32),#big platform 1
(1388,731,32,32),(1420,731,32,32),(1452,731,32,32),(1484,731,32,32),(1516,731,32,32),
(1548,731,32,32),(1580,731,32,32),(1612,731,32,32),(1644,731,32,32),(1676,731,32,32),
(1708,731,32,32),(1740,731,32,32),(1772,731,32,32),(1804,731,32,32),#big platform 2
]
WHITE=(255,255,255)
BLACK=(0,0,0)
RED=(255,0,0)
GREEN=(0,255,0)
BLUE=(0,0,255)
ORANGE=(255,128,0)
PURPLE=(127,0,255)
YELLOW=(255,255,0)
GRAY=(128,128,128)
BROWN=(102,51,0)
PINK=(255,102,178)
DARK_RED=(153,0,0)
DARK_BLUE=(0,0,153)
NIGHT_BLUE=(0,0,50)
LIGHT_YELLOW=(255,255,204)
LIGHT_GREEN=(204,255,204)
BROWN=(153,76,0)
NATURE_GREEN=(0,153,0)
SKY_BLUE=(0,204,204)
So yeah the platforms are supposed to move all together. If you want to run this code and see the problem for yourself i'm using the latest version of pygame.
As requested, here is the simplified main.py:
import random
import os
from settings import*
vec=pygame.math.Vector2
gamefile=os.path.dirname(__file__)
imagefile=os.path.join(gamefile,"sprites")
class Joueur(pygame.sprite.Sprite):
def __init__(self,jeu):
pygame.sprite.Sprite.__init__(self)
self.jeu=jeu
self.image=pygame.image.load(os.path.join(imagefile,"32character_1.png"))
self.rect=self.image.get_rect()
self.rect.center=(width/2,height/2)
self.pos=vec(width/2,700)
self.vel=vec(0,0)
self.acc=vec(0,0)
def saut(self):
self.rect.y+=1
coll=pygame.sprite.spritecollide(self,self.jeu.platform,False)
self.rect.y-=1
if coll:
self.vel.y=-18
def update(self):
self.acc=vec(0,0.8)
keys=pygame.key.get_pressed()
if keys[pygame.K_a]:
self.acc.x=-acc_joueur
if keys[pygame.K_d]:
self.acc.x=acc_joueur
self.acc.x+=self.vel.x*frict_joueur
self.acc.y+=self.vel.y*frict_air
self.vel+=self.acc
self.pos+=self.vel+0.5*self.acc
self.rect.midbottom=self.pos
class Grass(pygame.sprite.Sprite):
def __init__(self,x,y,w,h):
pygame.sprite.Sprite.__init__(self)
self.image=self.image=pygame.image.load(os.path.join(imagefile,"grass_block.png"))
self.rect=self.image.get_rect()
self.rect.x=x
self.rect.y=y
class Jeu:
def __init__(self):
pygame.init()
pygame.mixer.init()
self.cadre=pygame.display.set_mode((width,height))
self.clock=pygame.time.Clock()
self.running=True
def new(self):
self.allsprites=pygame.sprite.Group()
self.platform=pygame.sprite.Group()
self.joueur=Joueur(self)
self.allsprites.add(self.joueur)
for grass in grass_list:
p=Grass(*grass)
self.allsprites.add(p)
self.platform.add(p)
self.run()
def run(self):
self.playing=True
while self.playing:
self.clock.tick(fps)
self.events()
self.update()
self.draw()
def update(self):
self.allsprites.update()
coll=pygame.sprite.spritecollide(self.joueur,self.platform,False)
keys=pygame.key.get_pressed()
if self.joueur.vel.y>0:
if coll:
self.joueur.pos.y=coll[0].rect.top+1
self.joueur.vel.y=0
self.joueur.rect.midbottom=self.joueur.pos
if self.joueur.vel.y<0:
if coll:
self.joueur.vel.y*=-0.2
if self.joueur.rect.right>=width-350 and self.joueur.rect.right<width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)
if self.joueur.rect.right>=width-340 and keys[pygame.K_d]:
self.joueur.pos.x-=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x)+1
if self.joueur.rect.left<=width-450 and self.joueur.rect.left>width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)
if self.joueur.rect.left<=width-460 and keys[pygame.K_a]:
self.joueur.pos.x+=abs(self.joueur.vel.x)+1
for plat in self.platform:
plat.rect.x+=abs(self.joueur.vel.x)+1
if self.joueur.rect.bottom>height:
pygame.time.wait(500)
self.playing=False
def events(self):
for event in pygame.event.get():
if event.type==pygame.QUIT:
if self.playing:
self.playing=False
self.running=False
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_SPACE:
self.joueur.saut()
def draw(self):
self.cadre.fill(SKY_BLUE)
self.allsprites.draw(self.cadre)
pygame.display.update()
j=Jeu()
while j.running:
j.new()
pygame.quit()
And the simplified settings.py:
height=800
fps=60
acc_joueur=0.88
frict_joueur=-0.19
frict_air=-0.02
#(pos_x,pos_y,largeur,hauteur)
grass_list=[(300,700,32,32),(332,700,32,32),(364,700,32,32),(396,700,32,32),(428,700,32,32),
(268,700,32,32), #base platform
(492,638,32,32),(524,638,32,32),#lil platform 1
(652,576,32,32),(684,576,32,32),(716,576,32,32),(748,576,32,32),#lil platform 2
(940,514,32,32),(972,514,32,32),(1004,514,32,32),(1036,514,32,32),(1068,514,32,32),
(1100,514,32,32),(1132,514,32,32),#big platform 1
(1388,731,32,32),(1420,731,32,32),(1452,731,32,32),(1484,731,32,32),(1516,731,32,32),
(1548,731,32,32),(1580,731,32,32),(1612,731,32,32),(1644,731,32,32),(1676,731,32,32),
(1708,731,32,32),(1740,731,32,32),(1772,731,32,32),(1804,731,32,32),#big platform 2
]
WHITE=(255,255,255)
BLACK=(0,0,0)
RED=(255,0,0)
GREEN=(0,255,0)
BLUE=(0,0,255)
SKY_BLUE=(0,204,204)
解决方案
看起来您可能错过了以下方面的 +1:
self.joueur.pos.x-=abs(self.joueur.vel.x)+1 # here joueur gets +1
for plat in self.platform:
plat.rect.x-=abs(self.joueur.vel.x) # But the platforms do not :(
这是我现在唯一能看到的。让我知道是否可以解决它!
推荐阅读
- python - 您如何修复“缺少模块 docstringpylint(缺少模块文档字符串)”
- javascript - 反应上下文 api 中的问题
- java - 检查给定的数字是否令人着迷
- java - spring batch:如何动态创建步骤和任务
- c# - 如何在.net core中重命名环境名称
- c - 如何将文本从标准输入发送到脚本
- c# - 编辑报表工具生成的word文档报表
- python - 如何与 C 和 python 文件进行交互以复制数据?
- python - 删除 tkinter 文本小部件中的整个单词
- botframework - 使用 TranscriptLoggerMiddleware 记录附加信息