I am pretty new at coding, and am wondering how I would animate my sprite to look like it is actually moving. I think I have a general idea, but honestly I do not know where to start in my code to insert the different images and make it look like it's moving. This is my code, any tips are greatly appreciated.
SCREEN_SIZE = (1024, 768)
import sys
import pygame
from pygame.locals import *
from random import randint, choice
from Vector2 import Vector2
NEST_POSITION = (SCREEN_SIZE[0]/2, SCREEN_SIZE[1]/2)
NEST_POSITION_VECTOR2 = Vector2(NEST_POSITION)
ANT_COUNT = 20
NEST_SIZE = 100.
class State(object):
def __init__(self, name):
self.name = name
def do_actions(self):
pass
def check_conditions(self):
pass
def entry_actions(self):
pass
def exit_actions(self):
pass
class StateMachine(object):
def __init__(self):
self.states = {}
self.active_state = None
def add_state(self, state):
self.states[state.name] = state
def think(self):
if self.active_state is None: return
self.active_state.do_actions()
new_state_name = self.active_state.check_conditions()
if new_state_name is not None:
self.set_state(new_state_name)
def set_state(self, new_state_name):
if self.active_state is not None:
self.active_state.exit_actions()
self.active_state = self.states[new_state_name]
self.active_state.entry_actions()
class World(object):
def __init__(self):
self.entities = {}
self.entity_id = 0
self.background = pygame.surface.Surface(SCREEN_SIZE).convert()
self.background.fill((255, 255, 255))
pygame.draw.circle(self.background, (200, 255, 200), NEST_POSITION, int(NEST_SIZE))
def add_entity(self, entity):
self.entities[self.entity_id] = entity
entity.id = self.entity_id
self.entity_id += 1
def remove_entity(self, entity):
del self.entities[entity.id]
def get(self, entity_id):
if entity_id in self.entities:
return self.entities[entity_id]
else:
return None
def process(self, time_passed):
time_passed_seconds = time_passed / 1000.0
for entity in self.entities.values():
entity.process(time_passed_seconds)
def render(self, surface):
surface.blit(self.background, (0, 0))
for entity in self.entities.itervalues():
entity.render(surface)
def get_close_entity(self, name, location, range=100.):
for entity in self.entities.itervalues():
if (entity.name == name):
distance = location.get_distance_to(entity.location)
if (distance < range):
return entity
return None
############################# GameEntity ####################################
## This is a "template" object - that is, we never actually create one of
## these objects in the game.
## This object is used as a base templet for other game objects that "extend"
## or "inherit from" this object.
## The leaf, Spider and And objects all extend the GameEntity object
class GameEntity(object):
def __init__(self, world, name, image):
self.world = world
self.name = name
self.image = image
self.location = Vector2(0, 0)
self.destination = Vector2(0, 0)
self.speed = 0.
self.brain = StateMachine()
self.id = 0
def render(self, surface):
w, h = self.image.get_size()
surface.blit(self.image, (self.location.x-w/2, self.location.y-h/2))
def process(self, time_passed):
self.brain.think()
if ((self.speed > 0.) and (self.location != self.destination)):
vec_to_destination = self.destination - self.location
distance_to_destination = vec_to_destination.get_magnitude()
heading = vec_to_destination.get_normalized()
travel_distance = min(distance_to_destination, time_passed * self.speed)
self.location += heading * travel_distance
############################# Leaf ##########################################
class Leaf(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "leaf", image)
############################# Ant ##########################################
class Spider(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "spider", image)
self.dead_image = pygame.transform.flip(image, 0, 1)
self.health = 25
self.speed = 50. + randint(-20, 20)
def bitten(self):
self.health -= 1
if (self.health <= 0):
self.speed = 0.
self.image = self.dead_image
self.speed = 140.
def render(self, surface):
GameEntity.render(self, surface)
w, h = self.image.get_size()
bar_x = self.location.x - 12
bar_y = self.location.y + h/2
surface.fill( (255, 0, 0), (bar_x, bar_y, 25, 4))
surface.fill( (0, 255, 0), (bar_x, bar_y, self.health, 4))
def process(self, time_passed):
if (self.location.x > SCREEN_SIZE[0] + 2):
self.world.remove_entity(self)
return
GameEntity.process(self, time_passed)
############################# Ant ##########################################
class Ant(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "ant", image)
exploring_state = AntStateExploring(self)
seeking_state = AntStateSeeking(self)
delivering_state = AntStateDelivering(self)
hunting_state = AntStateHunting(self)
self.brain.add_state(exploring_state)
self.brain.add_state(seeking_state)
self.brain.add_state(delivering_state)
self.brain.add_state(hunting_state)
self.carry_image = None
def carry(self, image):
self.carry_image = image
def drop(self, surface):
if self.carry_image:
x = self.location.x
y = self.location.y
w, h = self.carry_image.get_size()
surface.blit(self.carry_image, (x-w, y-h/2))
self.carry_image = None
def render(self, surface):
GameEntity.render(self, surface)
if (self.carry_image):
x = self.location.x
y = self.location.y
w, h = self.carry_image.get_size()
surface.blit(self.carry_image, (x-w, y-h/2))
############################# AntStateExploring #############################
class AntStateExploring(State):
def __init__(self, ant):
State.__init__(self, "exploring")
self.ant = ant
def random_destination(self):
w, h = SCREEN_SIZE
self.ant.destination = Vector2(randint(0, w), randint(0, h))
def do_actions(self):
if (randint(1, 20) == 1):
self.random_destination()
def check_conditions(self):
leaf = self.ant.world.get_close_entity("leaf", self.ant.location)
if (leaf is not None):
self.ant.leaf_id = leaf.id
return "seeking"
spider = self.ant.world.get_close_entity("spider", NEST_POSITION_VECTOR2, NEST_SIZE)
if (spider is not None):
if (self.ant.location.get_distance_to(spider.location) < 100.):
self.ant.spider_id = spider.id
return "hunting"
return None
def entry_actions(self):
self.ant.speed = 120. + randint(-30, 30)
self.random_destination()
############################# AntStateSeeking ###############################
class AntStateSeeking(State):
def __init__(self, ant):
State.__init__(self, "seeking")
self.ant = ant
self.leaf_id = None
def check_conditions(self):
leaf = self.ant.world.get(self.ant.leaf_id)
if (leaf is None): return "exploring"
if (self.ant.location.get_distance_to(leaf.location) < 5.0):
self.ant.carry(leaf.image)
self.ant.world.remove_entity(leaf)
return "delivering"
return None
def entry_actions(self):
leaf = self.ant.world.get(self.ant.leaf_id)
if (leaf is not None):
self.ant.destination = leaf.location
self.ant.speed = 160. + randint(-20, 20)
############################# AntStateDelivering ############################
class AntStateDelivering(State):
def __init__(self, ant):
State.__init__(self, "delivering")
self.ant = ant
def check_conditions(self):
if (self.ant.location.get_distance_to(NEST_POSITION) < NEST_SIZE):
if (randint(1, 10) == 1):
self.ant.drop(self.ant.world.background)
return "exploring"
return None
def entry_actions(self):
self.ant.speed = 60.
random_offset = Vector2(randint(-20, 20), randint(-20, 20))
self.ant.destination = random_offset + NEST_POSITION
############################# AntStateHunting ###############################
class AntStateHunting(State):
def __init__(self, ant):
State.__init__(self, "hunting")
self.ant = ant
self.got_kill = False
def do_actions(self):
spider = self.ant.world.get(self.ant.spider_id)
if (spider is None): return
self.ant.destination = spider.location
if (self.ant.location.get_distance_to(spider.location) < 15.):
if (randint(1, 5) == 1):
spider.bitten()
if (spider.health <= 0):
self.ant.carry(spider.image)
self.ant.world.remove_entity(spider)
self.got_kill = True
def check_conditions(self):
if (self.got_kill): return "delivering"
spider = self.ant.world.get(self.ant.spider_id)
if (spider is None): return "exploring"
if (spider.location.get_distance_to(NEST_POSITION) > NEST_SIZE * 3):
return "exploring"
return None
def entry_actions(self):
self.speed = 160. + randint(0, 50)
def exit_actions(self):
self.got_kill = False
############################# Set up Game ###############################
def run():
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
pygame.display.set_caption("The Super Amazing Ant Simulation")
world = World()
w, h = SCREEN_SIZE
clock = pygame.time.Clock()
ant_image = pygame.image.load("ant.png").convert_alpha()
ant2_image = pygame.image.load("ant6.gif").convert_alpha()
ant3_image = pygame.image.load("ant7.gif").convert_alpha()
leaf_image = pygame.image.load("leaf.png").convert_alpha()
spider_image = pygame.image.load("spider.png").convert_alpha()
############# Place ants in starting locations #########################
for ant_no in xrange(ANT_COUNT):
ant = Ant(world, ant_image)
ant.location = Vector2(randint(0, w), randint(0, h))
ant.brain.set_state("exploring")
world.add_entity(ant)
############################# Game Loop ###############################
while (True):
for event in pygame.event.get():
if (event.type == QUIT):
pygame.quit()
return
time_passed = clock.tick(30)
if (randint(1, 50) == 1):
leafX = randint(0, w)
leafY = randint(0, h)
numberOfLeafs = randint(5,25)
for leafNumber in range(numberOfLeafs):
leaf = Leaf(world, leaf_image)
leaf.location = Vector2(leafX, leafY)
world.add_entity(leaf)
if (randint(1, 100) == 1):
spider = Spider(world, spider_image)
spider.location = Vector2(-50, randint(0, h))
spider.destination = Vector2(w+50, randint(0, h))
world.add_entity(spider)
world.process(time_passed)
world.render(screen)
pygame.display.update()
############################# Call run to start game #########################
try:
if (Vector2.version() != "Version 2014-04-09"):
raise()
except:
print "****ERROR**** Vector2 version mis-matched with AntSimulation.py"
sys.exit()
run()
In pygame, an animation is just multiple images played in a sequence. I'm assuming that because you are wanting to animate a sprite you already have an image sequence in mind to use.
Here is a basic example of an animation class (you will probably want to add quite a bit of functionality to this):
class Animation(object):
def __init__(self, image_list):
self.image_list = image_list #a list of pygame images
self.total_frames = len(image_list)
self.index = 0
def next(self):
image = self.image_list[self.index]
self.index = (self.index+1) % self.total_frames #loops back around
return image
Now, in your sprite class, when you are ready to switch to the next frame of your animation:
self.image = my_animation.next()
Related
I make a program where I generate circles of random size and position, by means of classes in python, I have managed to generate the circles as I wish but all collide with each other, so I have created a method so that this does not happen but it generates an error that I can not identify "'CreaCir' object has no attribute 'radio'", so I do not know what I'm doing wrong, the terminal tells me that the error is in the circle class in the collision method in the return, if someone could mark my error and give me advice to fix it I would appreciate it.
my code is as follows. first my circle class where I contain the collision method:
class Circulo(PosGeo):
def __init__(self, x, y, r):
self.x = x
self.y = y
self.radio = r
self.cx = x+r
self.cy = y+r
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.x, self.y), self.radio, 1)
def update(self):
pass
def colisionC(self, c2):
return self.radio + c2.radio > sqrt(pow(self.cx - c2.cx, 2) + pow(self.cy - c2.cy, 2))#where I get the error
now a part of my main code:
class CreaCir:
def __init__(self, figs):
self.i = 1
self.r = randint(5, 104)
self.x = randint(0, 500 + self.r)
self.y = randint(0, 300 + self.r)
self.creOne = Circulo(self.x, self.y, self.r)
self.figs.append(self.creOne)
def update(self):
if self.i <100:
choca = False
self.r = randint(5, 104)
self.x = randint(0, 500 + self.r)
self.y = randint(0, 300 + self.r)
self.creOne = Circulo(self.x, self.y, self.r)
for j in range (self.i):
choca = self.creOne.colisionC(self.figs[j])#here is where I use the collision method to my object
if choca == True:
break
if choca == False:
self.figs.append(self.creOne)
self.i+=1
def dibujar(self, ventana):
pass
called:
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.figs.append(CreaCir(self.figs))
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
pg.display.flip()
def run(self):
while True:
self.check_events()
for fig in self.figs:
fig.update()
for fig in self.figs:
fig.dibujar(self.ven)
self.reloj.tick(30)
if __name__ == '__main__':
ven = Ventana()
ven.run()
The cause of the error is that you have put a CreaCir object in the figs list. So the first item in figs (self.creOne.colisionC(self.figs[j])) is a CreaCir object and this object has no radio.
Just remove that line of code, it is absolutely unnecessary.
self.figs.append(CreaCir(self.figs))
Create the CreaCir in run, but don't put it in the list:
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.reloj = pg.time.Clock()
def check_events(self):
# [...]
def run(self):
cirCreater = CreaCir(self.figs)
while True:
self.check_events()
cirCreater.update()
for fig in self.figs:
fig.dibujar(self.ven)
self.reloj.tick(30)
To detect the collision of 2 circles, you need to calculate the distance between the centers of the circles and check if the distance is greater than the sum of the radii of the circles. The 3rd argument of pygame.draw.circle is the center of the circle, but not the top left of the bounding box of the circle:
pg.draw.circle(ventana, "white", (self.x, self.y), self.radio, 1)
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
Complete example:
import pygame as pg
import math, random
class Circulo:
def __init__(self, x, y, r):
self.x, self.y = x, y
self.radio = r
self.cx, self.cy = x+r, y+r
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
def colisionC(self, c2):
dx = self.cx - c2.cx
dy = self.cy - c2.cy
return self.radio + c2.radio > math.sqrt(dx*dx + dy*dy)
class CreaCir:
def __init__(self, figs):
self.figs = figs
def update(self):
if len(self.figs) < 100:
choca = False
r = random.randint(5, 104)
x = random.randint(0, 700 - r*2)
y = random.randint(0, 500 - r*2)
creOne = Circulo(x, y, r)
for fig in self.figs:
choca = creOne.colisionC(fig)
if choca == True:
break
if choca == False:
self.figs.append(creOne)
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.figs = []
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
def run(self):
cirCreater = CreaCir(self.figs)
while True:
self.check_events()
cirCreater.update()
self.ven.fill(pg.Color('#404040'))
for fig in self.figs:
fig.dibujar(self.ven)
pg.display.flip()
self.reloj.tick(30)
if __name__ == '__main__':
ven = Ventana()
ven.run()
I am trying to create a "Basketball game" using python and tkinter and I'm having trouble making my animation work. When I press the spacebar the basketball appears but it doesnt go upwards. I tried placing self.status.shoot == Status.shoot in multiple areas but the animation doesn't start once I hit the spacebar, instead the image of the basketball simply appears without movement. Any thoughts?
Here is my code:
model.py
import enum,math,random,time
# sizes of each images (pixels)
from Projects.MVC import controller
backboard1_height = 150
backboard1_width = 150
backboard2_height = 150
backboard2_width = 150
backboard3_height = 150
backboard3_width = 150
bg_height = 750
bg_width = 1000
floor_height = 180
floor_width = 1000
player_height = 250
player_width = 250
ball_height = 50
ball_width = 50
class Status(enum.Enum):
run = 1
pause = 2
game_over = 3
terminate = 4
shoot = 5
class HorizontalDirection(enum.IntEnum):
left = -1
none = 0
right = 1
class VerticalDirection(enum.IntEnum):
up = -1
none = 0
down = 1
class ImgDirection(enum.Enum):
left = -1
right = 1
class GameModel:
def __init__(self):
self.status = Status.pause
self.elements = []
self.next_time = time.time() # when we try drop a ball
# create elements
self.bg = Background(bg_width / 2, bg_height / 2)
self.floor = Floor(bg_width / 2, bg_height - (floor_height / 2))
self.backboard1 = Backboard1(bg_width / 2, player_height / 2)
self.backboard2 = Backboard2(bg_width / 10, player_height / 2)
self.backboard3 = Backboard3((bg_width / 2) + 400, player_height / 2)
self.player = Player(bg_width / 2, bg_height - floor_height )
self.text = TextInfo(80, 30)
self.init_elements()
def init_elements(self): # layer images on top of each other in order for every thing to be seen
self.elements = []
self.elements.append(self.bg)
self.elements.append(self.floor)
self.elements.append(self.backboard1)
self.elements.append(self.backboard2)
self.elements.append(self.backboard3)
self.elements.append(self.player)
self.elements.append(self.text)
def add_ball(self):
ball = Ball(self.player.x, self.player.y-125)
print("first self.player.y: {}".format(self.player.y))
#if self.status == Status.shoot:
self.elements.append(ball)
def random_ball_drop(self):
# if self.status == Status.shoot:
if self.next_time - time.time() < -2:
# if self.status == Status.shoot:
self.next_time = time.time() + 2
self.add_ball()
print("First time: {}".format(time.time()))
print("first Add.ball: {}".format(self.add_ball()))
#if self.status == Status.shoot:
elif self.next_time - time.time() < 0:
if random.uniform(0, 1) < 0.01:
self.next_time = time.time() + 2
self.add_ball()
print("Second time: {}".format(time.time()))
def check_status(self, element):
if type(element) is Ball:
dist = math.sqrt((element.x - self.backboard1.x) ** 2 + (element.y - self.backboard1.y) ** 2)
if dist < self.backboard1.catch_radius:
self.text.score += 1
return False
elif element.y >= bg_height:
self.text.lives -= 1
print("Text.lives: {}".format(self.text.lives))
return False
return True
def remove_ball(self):
self.elements = [e for e in self.elements if self.check_status(e)]
#print("self.element: {}".format(self.elements))
def update(self):
# if self.status == Status.shoot:
for element in self.elements:
element.update()
#print("first element.update: {}".format(element.update()))
# if self.status == Status.shoot:
self.random_ball_drop()
self.remove_ball()
print("Random_ball_drop from update block: {}".format(self.random_ball_drop()))
print("remove_ball: {}".format(self.remove_ball()))
def change_to_initial_state(self):
self.init_elements()
for e in self.elements:
e.change_to_initial_position()
class GameElement:
def __init__(self, x, y, direction_x, direction_y, speed):
self.initial_x = x
self.initial_y = y
self.x = x
self.y = y
self.direction_x = direction_x
self.direction_y = direction_y
self.speed = speed
def change_to_initial_position(self):
self.x = self.initial_x
self.y = self.initial_y
def update(self):
pass
class Background(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Floor(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
class Backboard1(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard1_height / 2) + (ball_height / 2) + 10
class Backboard2(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard2_height / 2) + (ball_height / 2) + 10
class Backboard3(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, 0)
self.catch_radius = (backboard3_height / 2) + (ball_height / 2) + 10
class Player(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=6)
self.img_direction = ImgDirection.left
def update(self):
if self.direction_x == HorizontalDirection.left:
if self.x > 0:
self.move()
elif self.direction_x == HorizontalDirection.right:
if self.x < bg_width:
self.move()
def move(self):
self.x += self.direction_x * self.speed
self.direction_x = HorizontalDirection.none
class Ball(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.up, speed=10)
def update(self):
self.y += self.direction_y*self.speed
print("This is self.y: {}".format(self.y))
class TextInfo(GameElement):
def __init__(self, x, y):
super().__init__(x, y, HorizontalDirection.none, VerticalDirection.none, speed=0)
self.score = 0
self.lives = 3
def change_to_initial_position(self):
self.score = 0
self.lives = 3
super().change_to_initial_position()
controller.py
from model import *
class GameController:
def __init__(self, model):
self.model = model
pass
def start_new_game(self):
self.model.change_to_initial_state()
self.model.status = Status.run
def continue_game(self):
self.model.status = Status.run
print(Status.run)
def exit_game(self):
self.model.status = Status.terminate
def press_p(self, event):
if self.model.status == Status.run:
self.model.status = Status.pause
print(Status.pause)
def press_left(self, event):
self.model.player.direction_x = HorizontalDirection.left
self.model.player.img_direction = ImgDirection.left
print(HorizontalDirection.left)
def press_right(self, event):
self.model.player.direction_x = HorizontalDirection.right
self.model.player.img_direction = ImgDirection.right
print(HorizontalDirection.right)
def press_space(self, event):
if self.model.status == Status.run:
self.model.status = Status.shoot
self.model.update()
print(Status.shoot)
def update_model(self):
if self.model.status == Status.run:
self.model.update()
view.py
import tkinter as tk
from PIL import ImageTk, Image
from model import *
class GameImages:
def __init__(self):
# background
self.bg_pil_img = Image.open('./resources/bg.png')
self.bg_img = ImageTk.PhotoImage(self.bg_pil_img)
# floor
self.floor_pil_img = Image.open('./resources/floor.png')
self.floor_img = ImageTk.PhotoImage(self.floor_pil_img)
# backboard1
self.backboard1_pil_img = Image.open('./resources/backboard1.png')
self.backboard1_pil_img = self.backboard1_pil_img.resize((backboard1_height, backboard1_width))
self.backboard1_img = ImageTk.PhotoImage(self.backboard1_pil_img)
# backboard2
self.backboard2_pil_img = Image.open('./resources/backboard2.png')
self.backboard2_pil_img = self.backboard2_pil_img.resize((backboard2_height, backboard2_width))
self.backboard2_img = ImageTk.PhotoImage(self.backboard2_pil_img)
# backboard3
self.backboard3_pil_img = Image.open('./resources/backboard3.png')
self.backboard3_pil_img = self.backboard1_pil_img.resize((backboard3_height, backboard3_width))
self.backboard3_img = ImageTk.PhotoImage(self.backboard3_pil_img)
# player
self.player_pil_img = Image.open('./resources/player.png')
self.player_pil_img_right = self.player_pil_img.resize((player_height, player_width))
self.player_pil_img_left = self.player_pil_img_right.transpose(Image.FLIP_LEFT_RIGHT)
self.player_img_right = ImageTk.PhotoImage(self.player_pil_img_right)
self.player_img_left = ImageTk.PhotoImage(self.player_pil_img_left)
# ball
self.ball_pil_img = Image.open('./resources/ball.png')
self.ball_pil_img = self.ball_pil_img.resize((ball_height, ball_width))
self.ball_img = ImageTk.PhotoImage(self.ball_pil_img)
def get_image(self, element):
if type(element) is Background:
return self.bg_img
if type(element) is Floor:
return self.floor_img
if type(element) is Backboard1:
return self.backboard1_img
if type(element) is Backboard2:
return self.backboard2_img
if type(element) is Backboard3:
return self.backboard3_img
if type(element) is Player:
if element.img_direction == ImgDirection.left:
return self.player_img_left
else:
return self.player_img_right
if type(element) is Ball:
return self.ball_img
return None
class DisplayGame:
def __init__(self, canvas, _id):
self.canvas = canvas
self.id = _id
def delete_from_screen(self):
self.canvas.delete(self.id)
class DisplayGameImage(DisplayGame):
def __init__(self, canvas, element, img):
super().__init__(canvas, canvas.create_image(element.x, element.y, image=img))
class DisplayGameText(DisplayGame):
def __init__(self, canvas, element):
text = "Score: %d\nLives: %d" % (element.score, element.lives)
super().__init__(canvas, canvas.create_text(element.x, element.y, font='12', text=text))
class DisplayMenu(DisplayGame):
def __init__(self, root, canvas, controller):
menu = tk.Frame(root, bg='grey', width=400, height=40)
menu.pack(fill='x')
new_game = tk.Button(menu, text="New Game", width=15, height=2, font='12', command=controller.start_new_game)
new_game.pack(side="top")
continue_game = tk.Button(menu, text="Continue", width=15, height=2, font='12', command=controller.continue_game)
continue_game.pack(side="top")
exit_game = tk.Button(menu, text="Exit Game", width=15, height=2, font='12', command=controller.exit_game)
exit_game.pack(side="top")
_id = canvas.create_window(bg_width / 2, bg_height / 2, window=menu)
super().__init__(canvas, _id)
class GameView:
def __init__(self, model, controller):
self.model = model
self.controller = controller
# root
self.root = tk.Tk()
self.root.title('Basketball Game')
# load images files
self.images = GameImages()
# canvas
self.canvas = tk.Canvas(self.root, width= bg_width, height= bg_height)
self.canvas.pack()
self.root.update()
# canvas elements id
self.elements_id = []
self.add_elements_to_canvas()
self.add_event_handlers()
self.is_menu_open = False
self.draw()
self.root.mainloop()
def add_elements_to_canvas(self):
for e in self.model.elements:
if type(e) is TextInfo:
self.elements_id.append(DisplayGameText(self.canvas, e))
else:
self.elements_id.append(DisplayGameImage(self.canvas, e, self.images.get_image(e)))
if self.model.status == Status.pause or self.model.status == Status.game_over:
self.elements_id.append(DisplayMenu(self.root, self.canvas, self.controller))
self.is_menu_open = True
def add_event_handlers(self):
self.root.bind("<Left>", self.controller.press_left)
self.root.bind("<Right>", self.controller.press_right)
self.root.bind("p", self.controller.press_p)
self.root.bind("<space>",self.controller.press_space)
def draw(self):
self.controller.update_model()
if self.model.status == Status.run or not self.is_menu_open:
self.is_menu_open = False
self.canvas.delete("all")
self.add_elements_to_canvas()
if self.model.status == Status.terminate:
self.root.destroy()
else:
self.canvas.after(5, self.draw)
I imagine this is a simple fix and have seen similar questions, but this is really frustrating me. Basically when you run the game, it only advances the simulation on a keypress, and not at a set framerate.
https://pastebin.com/aP6LsMMA
The main code is:
pg.init()
clock = pg.time.Clock()
FPS = 10
# ...
Game = Control()
while not Game.done:
Game.main_loop()
pg.display.update()
clock.tick(FPS)
pg.quit()
and the event handler method - within the Control class - is:
def event_handler(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.KEYDOWN:
self.scene.process_event(event)
The weird thing to me is that, at the bottom of the pastebin, is my old testing code that is not done with classes for Scenes/Control. However, to my eye it should work exactly the same. I've tried putting in the clock both in and out of the Control class to no avail.
Any help (and general tips!) greatly appreciated.
Thanks
process_event() should only change variables which depend on events but it shouldn't update other values which should be updated in every frame. You have to move some elements to new method update() and execute it in every loop.
More or less
class Scene:
def process_event(self, event):
pass
def update(self):
pass
class GamePlayState(Scene):
def process_event(self, event):
self.snake.get_key(event)
def update(self):
self.snake.update()
self.snake.food_check(self.apple)
self.snake.collision_check()
if self.snake.alive == False:
print("GAME OVER")
print(self.snake.points)
self.done = True
class Control:
def update(self):
self.scene.update()
def main_loop(self):
self.event_handler()
self.update()
self.scene_checker()
self.draw()
Full working code
import pygame as pg
import sys
import random
import queue
# TODO: Walls, queue for keypress, scene stuff, 2 player, difficulty(?)
""" ######################
PREAMBLE
###################### """
""" Dictionaries for direction/velocity mapping - stolen from https://github.com/Mekire """
DIRECT_DICT = {"left" : (-1, 0), "right" : (1, 0),
"up" : (0,-1), "down" : (0, 1)}
KEY_MAPPING = {pg.K_LEFT : "left", pg.K_RIGHT : "right",
pg.K_UP : "up", pg.K_DOWN : "down"}
OPPOSITES = {"left" : "right", "right" : "left",
"up" : "down", "down" : "up"}
""" Colour Mapping """
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
DARK_GREY = (70, 70, 70)
GREY = (211, 211, 211)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
COLOUR_MAP = {"snake": GREEN, "apple": RED, "wall": BLACK, "surface": GREY, "background": DARK_GREY }
""" ################
CLASSES
################ """
""" ####################### Object Classes ########################## """
class Square:
""" All other objects in the game will be built up from this """
def __init__(self, pos, colour, length):
self.xi, self.yi = pos # i for index, p for pixel
self.colour = colour
self.length = length
def display(self):
xp, yp = self.sq_to_pixs(self.xi, self.yi) # (x = left side, y = top edge)
pg.draw.rect(screen, self.colour, (xp, yp, self.length, self.length), 0)
def sq_to_pixs(self, x, y):
# Converts index of square to pixel coords
px = (x+1)*(2*MARGIN + SQUARE_SIZE) - MARGIN - SQUARE_SIZE
py = (y+1)*(2*MARGIN + SQUARE_SIZE) - MARGIN
return (px, py)
def index_coords(self): # TODO - remove for direct ref?
return (self.xi, self.yi)
class Arena:
""" A grid within which the game takes place """
def __init__(self, size, square_length, colour):
self.size = size # i.e. number of squares = size**2 for square arena
self.length = square_length # i.e. per square dimension
self.colour = colour
self.squares = [ [] for i in range(self.size) ]
for y in range(self.size):
for x in range(self.size):
self.squares[y].append(Square((x,y), self.colour, self.length))
def display(self):
for y in self.squares:
for square in y:
square.display()
class Snake:
""" Class for the agent(s) """
def __init__(self, pos, colour, square_length):
self.xi, self.yi = pos
self.colour = colour
self.size = 3
self.length = square_length
self.direction = "right"
self.direction_queue = queue.Queue(4) # TODO
self.points = 0
self.growing = False
self.alive = True
self.squares = []
for x in range(self.size): # horizontal initial orientation
self.squares.append(Square((self.xi - x, self.yi), self.colour, self.length))
def display(self):
for square in self.squares:
square.display()
def food_check(self, apple):
if self.squares[0].index_coords() == apple.square.index_coords():
self.growing = True
self.points += apple.points_value
apple.respawn([self])
def collision_check(self, walls = None):
xh, yh = self.squares[0].index_coords()
body = self.squares[-1:0:-1] # going backwards thru array as forwards [0:-1:1] didnt work...
def _collide(obstacles):
for sq in obstacles:
_x, _y = sq.index_coords()
if (_x == xh) and (_y == yh):
self.alive = False
_collide(body)
if walls is not None:
_collide(walls)
def update(self):
# Add new head based on velocity and old head
velocity = DIRECT_DICT[self.direction]
head_coords = [ (self.squares[0].index_coords()[i] + velocity[i]) for i in (0,1) ]
# Wrap around screen if reach the end
for i in (0, 1):
if head_coords[i] < 0:
head_coords[i] = SQUARES_PER_ARENA_SIDE - 1
elif head_coords[i] > SQUARES_PER_ARENA_SIDE - 1:
head_coords[i] = 0
self.squares.insert(0, Square(head_coords, self.colour, self.length))
if self.growing:
self.growing = False
else:
del self.squares[-1]
"""
def queue_key_press(self, key):
for keys in KEY_MAPPING:
if key in keys:
try:
self.direction_queue.put(KEY_MAPPING[keys], block=False)
break
except queue.Full:
pass
"""
class Player(Snake):
""" Human controlled snake via arrow keys """
def __init__(self, pos, colour, size):
Snake.__init__(self, pos, colour, size)
def get_key(self, event):
if event.type == pg.KEYDOWN and event.key in KEY_MAPPING:
new_direction = KEY_MAPPING[event.key]
if new_direction != OPPOSITES[self.direction]:
self.direction = new_direction
class Apple:
""" Food our (veggie) snake is greedily after """
def __init__(self, colour, length, points_value, snake):
self.colour = colour
self.length = length
self.xi, self.yi = self._rand_coords()
self.points_value = points_value
self.square = Square((self.xi, self.yi), self.colour, self.length)
def _rand_coords(self):
rand_num = lambda x: random.randint(0, x)
_x = rand_num(SQUARES_PER_ARENA_SIDE-1)
_y = rand_num(SQUARES_PER_ARENA_SIDE-1)
return _x, _y
def respawn(self, obstacles):
_x, _y = self._rand_coords()
for ob in obstacles:
for sq in ob.squares:
while sq.index_coords() == (_x, _y):
_x, _y = self._rand_coords()
self.square.xi, self.square.yi = _x, _y
def display(self):
self.square.display()
""" ################ SCENES ####################### """
class Scene:
""" Overload most of this - barebones structure
A bit pointless in current state but easily expanded """
def __init__(self):
self.done = False
def when_activated(self):
pass
def reset(self):
self.done = False
def render(self):
pass
def process_event(self, event):
pass
def update(self):
pass
class StartUp(Scene):
def __init__(self):
Scene.__init__(self)
def render(self):
# test placeholder
pass
def when_activated(self):
print("Press any key to continue")
def process_event(self, event):
if event.type == pg.KEYDOWN:
self.done = True
class GamePlayState(Scene):
def __init__(self):
Scene.__init__(self)
self.arena = Arena(SQUARES_PER_ARENA_SIDE, SQUARE_SIZE, COLOUR_MAP["surface"])
self.snake = Player(SNAKE_START, COLOUR_MAP["snake"], SQUARE_SIZE)
self.apple = Apple(COLOUR_MAP["apple"], SQUARE_SIZE, 1, self.snake)
self.font = pg.font.SysFont("courier new", 50)
def render(self):
screen.fill(COLOUR_MAP["background"])
self.arena.display()
self.apple.display()
self.snake.display()
text = self.font.render(str(self.snake.points), True, [255,255,255])
screen.blit(text, (500, 400))
def process_event(self, event):
self.snake.get_key(event)
def update(self):
self.snake.update()
self.snake.food_check(self.apple)
self.snake.collision_check()
if self.snake.alive == False:
print("GAME OVER")
print(self.snake.points)
self.done = True
""" ################## CONTROL CLASS #########################"""
class Control:
def __init__(self):
#self.clock = pg.time.Clock()
#self.fps = FPS
self.done = False
self.scene_array = [StartUp(), GamePlayState()]
self.scene_index = 0 # dirty way whilst dict method needs tinkering
#self.scene_dict = {"START": StartUp(), "GAME": GamePlayState()} #TODO
self.scene = self.scene_array[self.scene_index]
#self.scene = self.scene_dict["START"]
self.scene.when_activated()
def event_handler(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True
elif event.type == pg.KEYDOWN:
self.scene.process_event(event)
def scene_checker(self):
if self.scene.done:
self.scene.reset() # for reuse - TODO
self.scene_index = (self.scene_index + 1) % len(self.scene_array)
self.scene = self.scene_array[self.scene_index]
self.scene.when_activated()
#self.scene = self.scene_dict[self.scene.next]
def update(self):
self.scene.update()
def draw(self):
self.scene.render()
def main_loop(self):
self.event_handler()
self.update()
self.scene_checker()
self.draw()
""" ################ RUN GAME ################ """
""" Game paramaters """
SQUARE_SIZE = 20 # pixels
SQUARES_PER_ARENA_SIDE = 20 # squares
MARGIN = 2 # pixels
SNAKE_START = (int(SQUARES_PER_ARENA_SIDE/2), int(SQUARES_PER_ARENA_SIDE/2)) # square coords
pg.init()
clock = pg.time.Clock()
# Square.display() and a few others need a direct reference to "screen" TODO implement better
w, h = 620, 620
SCREEN_SIZE = [w, h]
FPS = 10
screen = pg.display.set_mode(SCREEN_SIZE)
Game = Control()
while not Game.done:
Game.main_loop()
pg.display.update()
clock.tick(FPS)
pg.quit()
Hej,
python tells me that it cannot find the global name "SCREEN" is not defined.
Traceback (most recent call last):
File "C:\Python27\AntGame.py", line 443, in
run()
File "C:\Python27\AntGame.py", line 413, in run
ant.brain.set_state("exploring")
File "C:\Python27\AntGame.py", line 65, in set_state
self.active_state.entry_actions()
File "C:\Python27\AntGame.py", line 253, in random_destination
w, h = SCREEN.SIZE
here is my code. Cannot find the trick of this. I am a newbie ..
#its an "ant TV" simulation
SCREEN_SIZE = (640, 480)
NEST_POSITION = (320, 240)
ANT_COUNT = 20
NEST_SIZE = 100.
import pygame
from pygame.locals import *
from random import randint, choice
from GamOBjects.vector2 import Vector2
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
class State(object):
def __init__(self, name):
self.name = name
def do_actions(self):
pass
def check_conditions(self):
pass
def entry_actions(self):
pass
def exit_actions(self):
pass
class StateMachine(object):
def __init__(self):
self.states = {}
self.active_state = None
def add_state(self, state):
self.states[state.name] = state
def think(self):
if self.active_state is None:
return
self.active_state.do_actions()
new_state_name = self.active_state.check_conditions()
if new_state_name is not None:
self.set_state(new_state_name)
def set_state(self, new_state_name):
if self.active_state is not None:
self.active_state.exit_actions()
self.active_state = self.states[new_state_name]
self.active_state.entry_actions()
class World(object):
def __init__(self):
self.entities = {}
self.entit_id = 0
self.background = pygame.surface.Surface(SCREEN_SIZE).convert()
self.background.fill((255, 255, 255))
pygame.draw.circle(self.background, (200, 255, 200), NEST_POSITION, int(NEST_SIZE))
def add_entity(self, entity):
self.entities[self.entity_id] = entity
entity.id = self.entity_id
self.entity_id += 1
def remove_entity(self, entity):
del self.entities[entity.id]
def get(self, entity_id):
if entity_id in self.entities:
return self.entities[entity_id]
else:
return None
def process(self, time_passed):
time_passed_seconds = time_passed / 1000.0
for entity in self.entitites.itervalues():
entity.process(time_passed_seconds)
def render(self, surface):
surface.blit(self.background, (0, 0))
for entity in self.entities.values():
entity.render(surface)
def get_close_entity(self, name, location, range=100.):
location = Vector2(*location)
for entity in self.entities.values():
if entity.name == name:
distance = location.get_distance_to(entit_location)
if distance < range:
return entity
return None
class GameEntity(object):
def __init__(self, world, name, image):
self.world = world
self.name = name
self.image = image
self.location = Vector2(0, 0)
self.destination = Vector2(0, 0)
self.speed = 0.
self.brain = StateMachine()
self.id = 0
def render(self, surface):
x, y = self.location
w, h = self.image.get_size()
surface.blit(self.image, (x-w/2, y-h/2))
def process(self, time_passed):
self.brain.think()
if speed > 0 and self.location != self.destination:
vec_to_destination = self.destination - self.location
distance_to_destination = vec_to_destination.get_length()
heading = vec_to_destination.get_normalized()
travel_distance = min(distance_to_destination, time_passed * self.speed)
self.location += travel_distance * heading
class Leaf(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "spider", image)
class Spider(GameEntity):
def __init__(self, world, image):
self.dead_image = pygame.transform .flip(image, 0, 1)
self.health = 25
self.speed = 50. + randint(-20, 20)
def bitten(self):
self.health -= 1
if self.health <= 0:
self.speed=0.
self.image = self.dead_image
self.speed = 140.
def render(self, surface):
GameEntity.render(self, surface)
x, y = self.location
w, h = self.image.get_size()
bar_x = x - 12
bar_y = y + h/2
surface.fill( (255, 0, 0), (bar_x, bar_y, 25, 4))
surface.fill( (0, 255, 0), (bar_x, bar_y, self.health, 4))
def process(self, time_passed):
x, y = self.location
if x > SCREEN_SIZE[0] + 2:
self.world.remove_entity(self)
return
GameEntity.process(self, time_passed)
class Ant(GameEntity):
def __init__(self, world, image):
GameEntity.__init__(self, world, "ant", image)
exploring_state = AntStateExploring(self)
seeking_state = AntStateSeeking(self)
delivering_state = AntStateDelivering(self)
hunting_state = AntStateHunting(self)
self.brain.add_state(exploring_state)
self.brain.add_state(seeking_state)
self.brain.add_state(delivering_state)
self.brain.add_state(hunting_state)
self.carry_image = None
def carry(self, image):
self.carry_image = image
def drop(self, surface):
if self.carry_image:
x, y = self.location
w, h = self.carry_image.get_size()
surface.blit(self.carry_image, (x-w, y-h/2))
self.carry_image = None
def render(self, surface):
GameEntit.render(self, surface)
if self.carry_image:
x, y = self.location
w, h = self.carry_image.get_size()
surface.blit(self.carry_image, (x-w, y-h/2))
class AntStateExploring(State):
def __init__(self, ant):
State.__init__(self, "exploring")
self.ant = ant
def random_destination(self):
w, h = SCREEN.SIZE
self.ant.destination = vector2(randint(0, w), randint(0, h))
def do_actions(self):
if randint(1, 20) ==1:
self.random_destination()
def check_conditions(self):
leaf = self.ant.world.get_close_entity("leaf", self.ant.location)
if leaf is not None:
self.ant.leaf_id = leaf_id
return "seeking"
spider = self.ant.world.get_close_entity("spider", NEST_POSITION, NEST_SIZE)
if spider is not None:
if self.ant.location.get_distance_to(spider.location) < 100.:
self.ant.spider_id = spider.id
return "hunting"
return None
def entry_actions(self):
self.ant.speed = 120. + randint(-30, 30)
self.random_destination()
class AntStateSeeking(State):
def __init__(self, ant):
State.__init__(self, "Seeking")
self.ant = ant
self.leaf_id = None
def check_conditions(self):
leaf = self.ant.world.get(self.ant.leaf_id)
if leaf is None:
return "exploring"
if self.ant.location.get_distance_to(leaf.location) < 5.0:
self.ant.carry(image)
self.ant.world.remove_entity
return "delivering"
def entry_actions(self):
leaf = self.ant.world.get(self.ant.leaf_id)
if leaf is not None:
self.ant.destination = leaf.location
self.ant.speed = 160. + randint(-20, 20)
class AntStateDelivering(State):
def __init__(self, ant):
State.__init__(self, "delivering")
self.ant = ant
def check_conditions(self):
if Vector2(*NEST_POSITION).get_distance_to(self.ant.location) < NEST_SIZE:
if (randint(1, 10) == 1):
self.ant.drop(self.ant.world.background)
return "exploring"
return None
def entry_actions(self):
self.ant.speed = 60.
random_offset = Vector2(randint(-20, 20), randint(-20, 20))
self.ant.destination = Vector2(*NEST_POSITION) + random_offset
class AntStateHunting(State):
def __init__(self, ant):
State.__init__(self, "hunting")
self.ant = ant
self.got_kill = False
def do_actions(self):
spider = self.ant.world.get(self.ant.spider_id)
if spider is None:
return
self.ant.destination = spider.location
if self.ant.location.get_distance_to(spider.location) < 15.:
if randint(1, 5) == 1:
spider.bitten()
if spider.health <= 0:
self.ant.carry(spider.image)
self.ant.world.remove_entity(spider)
self.got_kill = True
def check_conditions(self):
if self.got_kill:
return "delivering"
spider = self.ant.world.get(self.ant.spider_id)
if spider is None:
return "exploring"
if spider.location.get_distance_to(NEST_POSITION) > NEST_SIZE * 3:
return exploring
return None
def entry_actions(self):
self.speed = 160. + randint(0, 50)
def exit_actions(self):
self.geot_kill = False
def run():
pygame.init()
world = World()
w, h = SCREEN_SIZE
clock = pygame.time.Clock()
ant_image = pygame.image.load("ant.png").convert_alpha()
leaf_image = pygame.image.load("leaf.png").convert_alpha()
spider_image = pygame.image.load("spider.png").convert_alpha()
for ant_no in xrange(ANT_COUNT):
ant = Ant(world, ant_image)
ant.location = Vector2(randint(0, w), randint(0, h))
ant.brain.set_state("exploring")
world.add_entity(ant)
while True:
for event in pygame.event.get():
if event.type == QUIT:
return
time_passed = clock.tick(30)
if randint(1, 10) == 1:
leaf = Leaf(world, leaf_image)
leaf.location = Vector2(randint(0, w), randint(0, h))
world.add_entity(leaf)
if randint(1, 100) == 1:
spider = Spider(world, spider_image)
spider.location = Vector2(-50, randint(0, h))
spider.destination = Vector2(w+50, randint(0,h))
world.add_entity(spider)
world.process(time_passed)
world.render(screen)
pygame.display.update()
if name == "main":
run()
You have a reference to SCREEN.SIZE, but the variable is clearly called SCREEN_SIZE.
The problem is that you tried to reference the variable SCREEN_SIZE, but typed SREEN.SIZE:
def random_destination(self):
w, h = SCREEN.SIZE
self.ant.destination = vector2(randint(0, w), randint(0, h))
Python assumes it has to look for an object named SCREEN and then return its attribute SIZE. SCREEN, however, does not exist. Change it to:
def random_destination(self):
w, h = SCREEN_SIZE
self.ant.destination = vector2(randint(0, w), randint(0, h))
This will unpack the contents of SCREEN_SIZE into w and h.
TBH, this is quite a trivial error message. Just curious ... how come you write such a big chunk of code and don't understand the traceback?
How do you make this code work: Just have pyglet installed and change assassin1.png,assassin2.png and assassin3.png with the name of images stored in the directory where you saved this code to a file.
import pyglet
def sprite_type(type_ = "standing"):
if type_ == "moving-forward":
moving_forward_image_list = [pyglet.image.load('assassin2.png'), pyglet.image.load('assassin3.png')]
moving_forward_animation = pyglet.image.Animation.from_image_sequence(moving_forward_image_list, 0.3)
return moving_forward_animation
if type_ == "standing":
standing_animation = pyglet.image.load("assassin1.png")
return standing_animation
class Assassin(pyglet.sprite.Sprite):
def __init__(self, batch, img):
pyglet.sprite.Sprite.__init__(self, img, x = 50, y = 30)
class Game(pyglet.window.Window):
def __init__(self):
pyglet.window.Window.__init__(self, width = 315, height = 220)
self.batch_draw = pyglet.graphics.Batch()
self.player_dict = {"standing": Assassin(batch = self.batch_draw, img = sprite_type()), "moving-forward": Assassin(batch = self.batch_draw, img = sprite_type(type_ = "moving-forward"))}
self.player = self.player_dict["standing"]
self.fps_display = pyglet.clock.ClockDisplay()
self.keys_held = []
self.schedule = pyglet.clock.schedule_interval(func = self.update, interval = 1/60.)
def on_draw(self):
self.clear()
self.fps_display.draw()
self.batch_draw.draw()
self.player.draw()
def on_key_press(self, symbol, modifiers):
self.keys_held.append(symbol)
if symbol == pyglet.window.key.RIGHT:
self.player = self.player_dict["moving-forward"]
print "The 'RIGHT' key was pressed"
def on_key_release(self, symbol, modifiers):
self.keys_held.pop(self.keys_held.index(symbol))
self.player = self.player_dict["standing"]
def update(self, interval):
if pyglet.window.key.RIGHT in self.keys_held:
self.player.x += 50 * interval
if __name__ == "__main__":
window = Game()
pyglet.app.run()
This code displays assassin1.png while no key is held at position (50 30), when the right key is held the character moves forward and every 0.3 seconds the 2 images assassin2.png and assassin3.png are switched as to give the illusion of a walking character. Also the fps are shown.
Currently when you release the key the character will return to the original position (50, 30). When you press the key again the character moves from the position where the walking character stopped last.
I understand why this happens because the walking animation character and the standing character are two different instances of the same class.
I first thought using class variables I would be able to store the position. But I can't seem to come up with a working solution. Does anyone have any ideas what I could do?
Edit 1 (#Hugh Bothwell)
import pyglet
def sprite_type(type_ = "standing"):
if type_ == "moving-forward":
moving_forward_image_list = [pyglet.image.load('assassin2.png'), pyglet.image.load('assassin3.png')]
moving_forward_animation = pyglet.image.Animation.from_image_sequence(moving_forward_image_list, 0.3)
return moving_forward_animation
if type_ == "standing":
standing_animation = pyglet.image.load("assassin1.png")
return standing_animation
class Assassin(pyglet.sprite.Sprite):
def __init__(self, batch, img):
pyglet.sprite.Sprite.__init__(self, img, x = 50, y = 30)
def stand(self, batch, img):
self.batch = batch
self.img = img
def move(self, batch, img):
self.batch = batch
self.img = img
class Game(pyglet.window.Window):
def __init__(self):
pyglet.window.Window.__init__(self, width = 315, height = 220)
self.batch_draw = pyglet.graphics.Batch()
self.player = Assassin(batch = self.batch_draw, img = sprite_type())
self.fps_display = pyglet.clock.ClockDisplay()
self.keys_held = []
self.schedule = pyglet.clock.schedule_interval(func = self.update, interval = 1/60.)
def on_draw(self):
self.clear()
self.fps_display.draw()
self.batch_draw.draw()
self.player.draw()
def on_key_press(self, symbol, modifiers):
self.keys_held.append(symbol)
if symbol == pyglet.window.key.RIGHT:
self.player.move(batch = self.batch_draw, img = sprite_type("moving-forward"))
# if I put the following line here instead:
#self.player = self.player.move(batch = self.batch_draw, img = sprite_type("moving-forward"))
# I get this error when pressing the <right-direction> key:
#self.player.draw(), AttributeError: 'NoneType' object has no attribute 'draw'
print "The 'RIGHT' key was pressed"
def on_key_release(self, symbol, modifiers):
self.keys_held.pop(self.keys_held.index(symbol))
self.player.stand(batch = self.batch_draw, img = sprite_type("standing"))
def update(self, interval):
if pyglet.window.key.RIGHT in self.keys_held:
self.player.x += 50 * interval
if __name__ == "__main__":
window = Game()
pyglet.app.run()
Instead of having a standing-character and a walking-character, you need one character who has a movement-state.