我目前正在youtube上的视频'kidscancode'的帮助下编写一个平台游戏。到目前为止,我已经很顺利了,我收到了以下错误:
Traceback (most recent call last):
File "D:\College\CS Project\Python Coding For Project New\Main.py", line 290, in <module>
g.new()
File "D:\College\CS Project\Python Coding For Project New\Main.py", line 84, in new
Platform(self, *plat)
TypeError: __init__() takes 4 positional arguments but 6 were given
这是我的代码(分为3页,主页,精灵和设置):
主
>># The "new" section creates new objects that spawn into the game, such as the player's character.
def new(self):
# Start a new game
# Sets player's starting score to 0.
self.score = 0
# We add a sprite group
self.all_sprites = pg.sprite.Group()
# A platforms group
self.platforms = pg.sprite.Group()
# A powerups group
self.powerups = pg.sprite.Group()
# A players group
self.player = Player(self)
>for plat in PLATFORM_LIST:
# The next line takes the list platform on the settings page, and explodes it, gathering each variable from inside the list (this is just a shorter way of coding it)
Platform(self, *plat)
# Implements music into the game:
# The following line means that the music file is queued up and ready to play.
pg.mixer.music.load(path.join(self.sound_dir, 'gba1complete.ogg'))
self.run()
# The next run function is the start of my game loop.
def run(self):
# Game Loop
#Plays music:
#music.play() would normally only play the music track once, however there is a loop option. The -1 value means infinitely loop the track, which is what I want to happen.
pg.mixer.music.play(loops=-1)
self.playing = True
while self.playing:
# This will start the clock when the player starts playing
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
#Music ends, fadeout instead of stop (milliseconds)
pg.mixer.music.fadeout(3000)
def update(self):
# Game Loop - Update
self.all_sprites.update()
# The following initiates a collision check for the player's character:
# Checks if player hits a platform - only if falling (y velocity is above 0)
if self.player.vel.y > 0:
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
# Line below says that we are only going to snap to the platform if our feet are higher than the platform.
# REVISIT
# REVISIT
# REVISIT
# REVISIT
# REVISIT
# The following mini section explains how I prevented overlapping snaps causing problems.
lowest = hits[0]
# We then compare the other hits
for hit in hits:
# See if any are lower than the first:
if hit.rect.bottom > lowest.rect.centery:
lowest = hit
if self.player.pos.x < lowest.rect.right + 10 and \
self.player.pos.x > lowest.rect.left -10:
if self.player.pos.y < lowest.rect.centery:
# If the player has collided with an object, then we want the player to stop falling in the y direction at that point.
self.player.pos.y = lowest.rect.top
# The next line sets the character's velocity to 0, so they do not carry on falling through the object at all. This allows us to run along it also.
self.player.vel.y = 0
self.player.jumping = False
# If the player reaches right 1/4 of the screen
if self.player.rect.right >= WIDTH -400 :
# The next line moves the camera as the player moves (by the same velocity)
self.player.pos.x -= (self.player.vel.x)
# Next few lines move all of the platforms in the platform list on the settings page.
for plat in self.platforms:
# The next line means that the platforms stay behind once the player has moved.
plat.rect.x -= (self.player.vel.x)
# The next iteration loop is very similar to the previous one, however it works for the left side of the screen.
if self.player.rect.right <= WIDTH -700 :
# Here we set our boundary to the left (WIDTH - 700).
self.player.pos.x -= (self.player.vel.x)
# Then we move the platforms forwards, so we can retrace our steps in a level.
for plat in self.platforms:
plat.rect.x -= (self.player.vel.x)
if plat.rect.right <= WIDTH - 10000:
plat.kill()
# If the player dies:
# If the player hits the bottom of the screen:
if self.player.rect.bottom > HEIGHT:
# Next loop scrolls platforms upwards when player falls off the map to make the game more realistic.
for sprite in self.all_sprites:
# We do not want to scroll the platforms at a constant speed, as this is not realistic, however we want to set a max number, and using the max function here means we pick the max number between the player's vel and 10.
sprite.rect.y -= max(self.player.vel.y, 10)
# The next two lines kill all sprites when they reache the bottom of the screen.
if sprite.rect.bottom < 0:
sprite.kill()
# The next line ends the game when the legnth of the platforms is zero, as we have gotten rid of them all.
if len(self.platforms) == 0:
# The next line lets the game know that the player's character is dead, and therefore ends the game session.
self.playing = False
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
# Below is an IF loop that determines if the player's character should be jumping or not.
if event.type == pg.KEYDOWN:
# First we establish that a key is being pressed, and then identify if it is the spacebar (or jump button)
if event.key == pg.K_SPACE:
self.player.jump()
def draw(self):
# Game Loop - draw
self.screen.fill(CYAN)
self.all_sprites.draw(self.screen)
self.draw_text(str(self.score), 22, BLACK, WIDTH / 2, 15)
# *after* drawing everything, flip the display
pg.display.flip()
def show_start_screen(self):
# game splash/start screen
#Loads music again, can use a different track
pg.mixer.music.load(path.join(self.sound_dir, 'gba1complete.ogg'))
pg.mixer.music.play(loops=-1)
# Fill the start screen background colour as brown to match our survival theme
self.screen.fill(BROWN)
# We display our string for 'TITLE', then choose font size, colour and choose where to display it on the screen.
self.draw_text(TITLE, 48, WHITE, WIDTH / 2, HEIGHT / 4)
# We display some information on how to play the game to the user
self.draw_text("A and D Keys to move, Spacebar to jump!", 24, WHITE, WIDTH /2, HEIGHT / 2)
# We tell the user how to initiate the game
self.draw_text("Press any key to play", 24, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# We display the high score on the starting screen:
self.draw_text("High Score: " + str(self.highscore), 22, BROWN, WIDTH / 2, 15)
# We flip the display so that the user actually sees this taking place
pg.display.flip()
self.wait_for_key()
# This time I want to music to stop immediately.
pg.mixer.music.stop
def show_go_screen(self):
# game over/continue
if not self.running:
# Return meaning end the function
return
# music
pg.mixer.music.load(path.join(self.sound_dir, 'gba1complete.ogg'))
pg.mixer.music.play(loops=-1)
# Choose a background colour for the end screen
self.screen.fill(BROWN)
# Display game over
self.draw_text("Game Over", 48, WHITE, WIDTH / 2, HEIGHT / 4)
# Display the users score
self.draw_text("Score: " + str(self.score), 24, WHITE, WIDTH /2, HEIGHT / 2)
# Tell them how to restart the game
self.draw_text("Press any key to play again", 24, WHITE, WIDTH / 2, HEIGHT * 3 / 4)
# The following small section of code determines what message the player recieves - whether the player gets congratulated or commiserations.
if self.score > self.highscore:
self.highscore = self.score
# The following line explains to the player that they have just got a new high score:
self.draw_text("New High Score!", 22, BROWN, WIDTH / 2, HEIGHT / 2 + 40)
with open(path.join(self.dir, HS_FILE), 'w') as f:
# Then we write over the high score file and save it as a string because it is a text file.
f.write(str(self.score))
# Following line shows what happens if the player does not get a high score (displays current high score).
else:
self.draw_text("High Score: " + str(self.highscore), 22, BROWN, WIDTH / 2, HEIGHT / 2 + 40)
# Enable the user to see this
pg.display.flip()
# Tells the program to stop and waits for the player to input a key to continue
self.wait_for_key()
pg.mixer.music.stop
# The following method can be used in both show_start_screen and show_go_screen (end screen) to make the game wait for the player to press a key.
def wait_for_key(self):
# We set a variable to true.
waiting = True
# Then we create a while loop so that we can choose when the game waits and when the game continues.
while waiting:
# The next line chooses to run the start/end screen at the chosen FPS. We have defined FPS in the settings page so I will use this.
self.clock.tick(FPS)
for event in pg.event.get():
if event.type == pg.QUIT:
# To leave this screen when the player presses QUIT, we end our loop
waiting = False
# Then because we want our whole program to end, we also need to set running to false.
self.running = False
# Any other key on the keyboard
if event.type == pg.KEYUP:
waiting = False
#The following method shows the text in my game.
def draw_text(self, text, size, colour, x, y):
# The next line gets the font object.
font = pg.font.Font(self.font_name, size)
# Next we render a surface for the font to be displayed on.
# The 'True' value is my choice of whether to use anti-aliasing or not, I chose true as I believe it will make my game look smoother.
text_surface = font.render(text, True, colour)
# Then we generate a rectangle to help us generate it on the screen.
text_rect = text_surface.get_rect()
# Next we place it
text_rect.midtop = (x, y)
# Then we blit it onto the screen
self.screen.blit(text_surface, text_rect)
>g = Game()
g.show_start_screen()
while g.running:
g.new()
g.show_go_screen()
pg.quit()
精灵:
>import pygame as pg
from Settings import *
# This allows pygame to access vectors.
vec = pg.math.Vector2
# Technically this is not a sprite, but it is another class to define so:
class Spritesheet:
# Utility class for loading and parsing (reading through the data file, understanding what it means) spritesheets
# We will only need to pass it the filename
def __init__(self, filename):
# Here we load the spritesheet's filename and convert it
self.spritesheet = pg.image.load(filename).convert()
# The following function lets us pick an image from the spritesheet.
def get_image(self, x, y, width, height):
# First we make a surface to put the image on:
image = pg.Surface((width, height))
# image.blit - Blit it onto this image # (self.spritesheet, - We take this spritesheet
#(0, 0), - Blit it at this location # (x, y, width, height)) - Choose this chunk
image.blit(self.spritesheet, (0, 0), (x, y, width, height))
# Then we return the image
# Below I will scale my image so that the character is the correct size for the game, as initially it was far too large.
# The double slash rounds the number to the nearest integer, so there are no errors.
image = pg.transform.scale(image, (width // 2, height // 2))
return image
# The following section creates the image/character model e.g. size etc.
class Player(pg.sprite.Sprite):
# The use of 'game' in the parameter below means that the player knows about the variables from the 'main page'.
def __init__(self, game):
# Adds the player class to the group.
self.groups = game.all_sprites
# Here we set up the Sprite object.
pg.sprite.Sprite.__init__(self, self.groups)
# The line below is a 'copy' or a referance that the player class can use so that it knows about the game.
self.game = game
# The following line lets the game know whether the player is walking.
self.walking = False
# The following line lets the game know whether the player is jumping.
self.jumping = False
# The following line asks the game what frame is currently playing.
self.current_frame = 0
# The following variable keeps track of what time we made the last change, as each frame would be too fast, (60 per second!)
self.last_update = 0
# We choose for the player's image to be at the forefront, rather than the background.
# The load_images method is called shortly after this section of code.
self.load_images()
self.image = self.standing_frames[0]
# The line below removes the unwanted border around the player's character.
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
# For example, the next line determines where the center of the rectangle that we have just created is, so that the game knows how to affect movement/hitboxes for the player's character.
self.rect.center = (WIDTH / 2, HEIGHT /2)
self.pos = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
# The following method load_images allows me to load all of the frames that are used for the character's animation.
def load_images(self):
# I start with the two standing frames:
self.standing_frames = [self.game.spritesheet.get_image(614, 1063, 120, 191),
self.game.spritesheet.get_image(690, 406, 120, 201)]
for frame in self.standing_frames:
frame.set_colorkey(BLACK)
# Now I am going to assign my two walking frames for walking right:
self.walk_frames_r = [self.game.spritesheet.get_image(678, 860, 120, 201),
self.game.spritesheet.get_image(692, 1458, 120, 207)]
# Now walk frames for the left direction:
self.walk_frames_l = []
# Now we will append the right ones to the left facing lists, and flip them:
for frame in self.walk_frames_r:
frame.set_colorkey(BLACK)
# Here the true and false represent whether I want a horizontal flip and then a vertical flip.
self.walk_frames_l.append(pg.transform.flip(frame, True, False))
# Now the jumping frame:
self.jump_frame = self.game.spritesheet.get_image(382, 763, 150, 181)
self.jump_frame.set_colorkey(BLACK)
# The following is a jump function that allows the player to gain velocity in the upwards direction.
def jump(self):
# The line below makes the character collide with the platform, however there is no drawing so the player doesn't see this.
self.rect.x += 1
# The next line initiates the collision, as we have done before. We collide the character with the game's platforms.
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
# The following line means that our character is moved back up 3 spaces. The player never sees this because no drawing of it is done.
self.rect.x -= 3
# The next few lines mean that if there is collision, we can jump.
if hits:
# Now we play the jump sound at the same time as jumping:
self.game.jump_sound.play()
# The value below is the strength of the jump.
self.vel.y = -PLAYER_JUMP
# The next section creates player movement. E.g. when certain keys are pressed, the character moves in the corresponding direction.
def update(self):
# The following method animates and handles which frame we want to use:
self.animate()
# Below I have set vertical acceleration (gravity) to the variable PLAYER_GRAV, which can be found in the Settings page, allowing the character to fall.
self.acc = vec(0, PLAYER_GRAV)
keys = pg.key.get_pressed()
# Here, if the left key is pressed, the player will move left and so on.
if keys[pg.K_a]:
self.acc.x = -PLAYER_ACC
if keys[pg.K_d]:
self.acc.x = PLAYER_ACC
# Applies friction by reducing acceleration
# By adding .x after self.acc and self.vel, gravity is allowed to work properly as we can speed up faster in the y direction.
self.acc.x += self.vel.x * PLAYER_FRICTION
# Equations of motion
self.vel += self.acc
# The following two lines mean that if we are moving less than 0.1 of a pixel per tick, the player's velocity will get set to 0. This is so that our walking animations stop when we are standing still.
if abs(self.vel.x) < 0.1:
# Velocity is set to 0.
self.vel.x = 0
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
# The next line lets the game know that I want the center of the player's character to be considered the center of the screen.
self.rect.midbottom = self.pos
# The recently referred to animate method is used:
def animate(self):
# Find out how many ticks have occured since the game was opened:
now = pg.time.get_ticks()
# The following small section determines if the player is standing still or not.
# Is the player still?
if self.vel.x != 0:
# If they are not still, then walking is true.
self.walking = True
else:
# If they are still, then walking is false.
self.walking = False
# Show Walk Animation:
if self.walking:
# here we set the frequency of the occuring updates (milliseconds)
if now - self.last_update > 100:
# We call for an update
self.last_update = now
# We almost copy what we used for the update loop below
self.current_frame = (self.current_frame + 1) % len(self.walk_frames_l)
# We set the bottom of the sprite to the right location whilst we are walking
bottom = self.rect.bottom
# Before we set the image for the next frame, we decide if we are walking left or right:
if self.vel.x > 0:
# If our velocity is more than 0, we are clearly moving right
self.image = self.walk_frames_r[self.current_frame]
else:
# If our velocity is less than 0, we are clearly moving left
self.image = self.walk_frames_l[self.current_frame]
# We request our new rectangle to find the bottom of the character with
self.rect = self.image.get_rect()
# We then set the bottom of the character's hitbox again.
self.rect.bottom = bottom
# If the player is idle, we want to display the standing frame:
if not self.jumping and not self.walking:
# The following is in milliseconds, and determines the time lapse since the last update:
if now - self.last_update > 280:
# We call for an update to occur now:
self.last_update = now
# Now we change the current frame.
self.current_frame = (self.current_frame + 1) % len(self.standing_frames)
# The bottom variable holds where the bottom of our player's rectangle needs to be.
bottom = self.rect.bottom
self.image = self.standing_frames[self.current_frame]
# the following line gets the new rectangle for the new frame we switched to.
self.rect = self.image.get_rect()
# the next line keeps the 'bottom' variable that we had for the last frame.
self.rect.bottom = bottom
# The next class defines all of the platforms in the game.
class Platform(pg.sprite.Sprite):
# The next line shows how when we want to create a new platform object, we will give it a x and a y coordinate, along with a width and a height.
def __init__(self, game, x, y):
# Adds the platform class to the group.
self.groups = game.all_sprites, game.platforms
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
# First, we create the image.
self.image = pg.Surface((w, h))
# Hext, we choose a colour for the platform.
self.image.fill(BROWN)
# We make it a rectangular shape.
self.rect = self.image.get_rect()
# Then we place it at it's predetermined coordinates.
self.rect.x = x
self.rect.y = y
#Powerups class
class Pow(pg.sprite.Sprite):
# I pass the class 'self', 'game' and 'plat'.
def __init__(self, game, plat):
# I create my list of groups:
self.groups = game.all_sprites, game.powerups
# I then initialise them (including groups):
pg.sprite.Sprite.__init__(self, self.groups)
# The following line allows me to use 'game'
self.game = game
# The following line allows me to use 'plat'
self.plat = plat
# Now we need different types of platforms.
# The first type I am creating is called 'boost', it will shoot the player upwards a litte bit.
self.type = choice(['boost'])
# This is the art that will be used for my 'boost' powerup.
self.image = self.game.spritesheet.get_image(820, 1805, 71, 70)
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
# The following line sets the powerup's location to the center of the platform.
self.rect.centerx = self.plat.rect.centerx
self.rect.bottom = self.plat.rect.top - 5
def update(self):
self.rect.bottom = self.plat.rect.top -5
if not self.game.platforms.has(self.plat):
# If this loop returns false, we will delete the powerup as well.
self.kill()
设置:
>TITLE = "Survival Game"
WIDTH = 1000
HEIGHT = 800
FPS = 60
HS_FILE = "highscore.txt"
PLAYER_ACC = 0.5
PLAYER_FRICTION = -0.12
PLAYER_GRAV = 0.3
PLAYER_JUMP = 10
FONT_NAME = 'Echelon'
SPRITESHEET = "spritesheet_jumper.png"
# Starting platforms
# The following list includes all the platforms I am going to be using in my game, and each platform takes in four values each. The x and y coordinates, and also the height and width of the platforms themselves.
# (x, y, Width, Height)
PLATFORM_LIST = [
# Floor platforms
(0, HEIGHT - 40, WIDTH + 500, 40),
(2200, HEIGHT - 40, WIDTH + 100, 40),
(4000, HEIGHT - 40, WIDTH + 100, 40),
(6000, HEIGHT - 40, WIDTH + 100, 40),
# Starting 3 platforms
(650, HEIGHT - 125, WIDTH - 600, 20),
(650, HEIGHT - 250, WIDTH - 600, 20),
(650, HEIGHT - 375, WIDTH - 600, 20),
# Middle Layer of short platforms
(1100, HEIGHT - 250, WIDTH - 800, 30),
(1500, HEIGHT - 250, WIDTH - 800, 30),
(2000, HEIGHT - 250, WIDTH - 800, 30),
(2500, HEIGHT - 250, WIDTH - 800, 30),
(3000, HEIGHT - 250, WIDTH - 800, 30),
(3500, HEIGHT - 250, WIDTH - 800, 30),
# Lower Layer of short platforms
(1175, HEIGHT - 180, WIDTH - 800, 30),
(1575, HEIGHT - 180, WIDTH - 800, 30),
(2075, HEIGHT - 180, WIDTH - 800, 30),
(2575, HEIGHT - 180, WIDTH - 800, 30),
(3075, HEIGHT - 180, WIDTH - 800, 30),
(3575, HEIGHT - 180, WIDTH - 800, 30),
# Upper Layer of short platforms
(1150, HEIGHT - 320, WIDTH - 800, 30),
(1550, HEIGHT - 320, WIDTH - 800, 30),
(2050, HEIGHT - 320, WIDTH - 800, 30),
(2550, HEIGHT - 320, WIDTH - 800, 30),
(3050, HEIGHT - 320, WIDTH - 800, 30),
(3550, HEIGHT - 320, WIDTH - 800, 30),
# In between platforms (Lowest)
(1250, HEIGHT - 115, WIDTH - 800, 30),
(1750, HEIGHT - 115, WIDTH - 800, 30),
(2250, HEIGHT - 115, WIDTH - 800, 30),
(2750, HEIGHT - 115, WIDTH - 800, 30),
(3250, HEIGHT - 115, WIDTH - 800, 30),
(3750, HEIGHT - 115, WIDTH - 800, 30)
]
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
CYAN = (0, 255, 255)
BROWN = (200, 140, 101)
我注意到当我从中删除self
参数时
PLATFORM_LIST
:
Platform(self, *plat)
,
该错误变成了有5个参数(而不是6个)的事实,其中只有4个。
答案 0 :(得分:2)
您必须将参数(game, x, y)
传递给Platform
构造函数。
*plat
似乎是四个元素,而不是(x,y)
对,因此您的错误显示为6
self
(默认情况下已通过)game
这是相关代码
# (x, y, Width, Height)
PLATFORM_LIST = [
# Floor platforms
(0, HEIGHT - 40, WIDTH + 500, 40),
...
]
...
class Platform(pg.sprite.Sprite):
def __init__(self, game, x, y):
# ...
...
for plat in PLATFORM_LIST:
Platform(self, *plat)