首页 > 解决方案 > 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)

标签: pythonpygame

解决方案


看起来您可能错过了以下方面的 +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 :(

这是我现在唯一能看到的。让我知道是否可以解决它!


推荐阅读