Updating object on Python < arcade library > - python

I'm currently coding in some assignment.
I create a class named Ship and created some methods to make the object class ship be able to move.
class Ship():
def __init__(self):
self.center.x = 400
self.center.y = 300
self.velocity.dx = .25
self.velocity.dy = .25
def advance_down(self):
self.center.y = self.center.y - self.velocity.dy
This is my code
def check_keys(self):
"""
This function checks for keys that are being held down.
You will need to put your own method calls in here.
"""
if arcade.key.DOWN in self.held_keys:
self.ship1.advance_down()
advance down just changes the position
def on_key_press(self, key: int, modifiers: int):
"""
Puts the current key in the set of keys that are being held.
You will need to add things here to handle firing the bullet.
"""
if key == arcade.key.DOWN:
self.held_keys.add(key)
This adds the current key being pressed to the set of self.held_keys.
I'm calling an update of the positon.
def update(self, delta_time):
"""
Update each object in the game.
:param delta_time: tells us how much time has actually elapsed
""
self.ship1.advance_down()
when I run the code I'm able to display everything just fine but it's not doing anything once I press my keys.
Any ideas why?

Related

Execute only once in python

So in my game I have a class called MovementService. This class handles moving an object when the uses presses an arrow key. However, when you press the up arrow 5 times in a row real quick, the object will move one square 5 times. This feels weird, because you're already done with pressing, but the character is still moving.
So I went and added some code to the class to try and prevent that. But now the movement is only executed once. But thing is, it's the first movement. How can I make sure it's the last press?
class MovementService():
lastExecutionEpoch = 0
minimumDifference = 1
def moveUp(self):
self.execute('MOVEUP')
def moveDown(self):
self.execute('MOVEDOWN')
def execute(self, movement):
if self.lastExecutionEpoch < (int(time.time()) - self.minimumDifference):
self.lastExecutionEpoch = int(time.time())
...
Do the actual movement
...

Game with turns, need a way to receive a fuction's return in another turn

I'm making a game where I can gather resources or build when I send Workers, but I can't think of a way to receive those resources or finish building depending on the turn and the time(turns) it takes to finish those actions.
I've already made a Worker class, and it has a method to gather and it gives a random value that I save in a Player class. Also, my Game class keeps track of the turn I and the computers are.
class Game:
def __init__(self, player = None):
self.player = player
self.turn = 1
class Player:
def __init__(self):
self.workers = [Worker(), Worker(), Worker()]
self.resourcers = 0
class Worker:
def __init__(self):
self.hp = 100
def gather(self):
return randint(MIN_CANTIDAD_RECURSO, MAX_CANTIDAD_RECURSO)
player = Player()
game = Game()
game.player = player
for worker in player.workers:
player.resources += worker.gather
game.turn +=1
Gathering should give the result the next turn and build should give it depending on the building.
In a general sense, you store the values you need in the relevant object and pass them as parameters to whatever method requires those values. For example, you would need to store the turn duration of an action in the return value of that action, e.g in class Worker
def gather(self):
# Some code that determines gather_value and duration...
return [gather_value, duration]
and then the resource usage would look something like
def use_gather(gather, turn): # Pass in (return value from gather, game.turn)
# Use parameters...
With such a vague question, it's hard to say anything more.

How do I program a double key press to make a non-modifier key behave like a modifier key within my program?

I am writing a typing program that includes many more characters than are available on a standard keyboard. In order to achieve this I need to transform some of the alphabet keys into modifier keys CTRL+A. For example f+j would output a. Typing f then j is slow for the user, I need them to be able to press f and j at the same time and receive one output. It's fine (preferable even) if some of the keyboard's normal functionality is stopped while the program is running.
I have looked into pygame Keydown, but it only seems to have functions for increasing key repeat and not stopping key output. Pyglet is also a possibility, but it doesn't have exact documentation on how I could make additional modifier keys. The only way I can figure out is to be constantly scanning the whole keyboard to see if any keys are pressed, but that won't determine the order the keys are pressed in and will create errors for the user, as the user pressing f then j would be read the same as the user pressing j then f and I need only the f then j combo to be understood as a keystroke by the system.
Here's a Pyglet version of how you could do it.
I based it on common GUI class that I use often here on SO because it's modular and easier to build on without the code getting messy after 40 lines.
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False)
self.x, self.y = 0, 0
#self.bg = Spr('background.jpg')
self.output = pyglet.text.Label('',
font_size=14,
x=self.width//2, y=self.height//2,
anchor_x='center', anchor_y='center')
self.alive = 1
self.pressed = []
self.key_table = {213 : 'a'}
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_release(self, symbol, modifiers):
if symbol == key.LCTRL:
pass # Again, here's how you modify based on Left CTRL for instance
## All key presses represents a integer, a=97, b=98 etc.
## What we do here is have a custom key_table, representing combinations.
## If the sum of two pressed matches to our table, we add that to our label.
## - If no match was found, we add the character representing each press instead.
## This way we support multiple presses but joined ones still takes priority.
key_values = sum(self.pressed)
if key_values in self.key_table:
self.output.text += self.key_table[key_values]
else:
for i in self.pressed:
self.output.text += chr(i)
self.pressed = []
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.LCTRL:
pass # Modify based on left control for instance
else:
self.pressed.append(symbol)
def render(self):
self.clear()
#self.bg.draw()
self.output.draw()
self.flip()
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze
#
event = self.dispatch_events()
x = main()
x.run()
It might look like a lot of code, especially to the Pygame answer. But you could condense this down to ~15 lines as well, but again, the code would get messy if you tried to build on it any further.
Hope this works. Now I haven't thought the math through on this one.. It might be possible that two duplicate key combinations will produce the same value as another key representation, simply replace the dictionary keys 213 for instance with a tuple key such as self.key_table = {(107, 106) : 'a'} which would represent k+j
Few benefits:
No need to keep track of delay's
Fast and responsive
Any key could be turned into a modifier or map against custom keyboard layouts, meaning you could turn QWERTY into DWORAK for this application alone.. Not sure why you would want that, but hey.. None of my business :D
Overrides default keyboard inputs, so you can intercept them and do whatever you want with them.
Edit: One cool feature would be to register each key down but replace the last character with the joined combination.. Again this is all manual works since a keyboard isn't meant to do double-key-representations, and it's more of a graphical idea.. But would be cool :)
Here is some simple code to print keys pressed in quick succession, written in Python 2. It should be able to easily be modified to suit your needs:
import pygame, sys
pygame.init()
screen = pygame.display.set_mode([500,500])
clock = pygame.time.Clock()
combokeys = []
timer = 0
ACCEPTABLE_DELAY = 30 #0.5 seconds
while 1:
clock.tick(60)
timer += 1
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if timer <= ACCEPTABLE_DELAY:
combokeys.append(event.unicode)
else:
combokeys = [event.unicode]
timer = 0
print combokeys
I have not been able to test this code (working at school computer), so please notify me in the comments if something did not work so I can fix it.
You can change the value given for ACCEPTABLE_DELAY to change the delay before something is considered a different key combination. The delay should be (ACCEPTABLE_DELAY/60) seconds.

Handle animations within an MVC pattern using Pygame

I am currently trying to implement an MVC pattern using Python and Pygame, but I can't figure out how to properly handle animations. Let's say we have a model object that can attack:
class ModelObject:
def attack():
if not self.attacking:
self.attacking = True
# Then compute some stuff
def isattacking():
return self.attacking
And a view object that renders the model object by displaying an attack animation:
class ViewObject(pygame.Sprite):
attacking_ressource = [] # List of surfaces for animation
default_ressource = None
def update():
# Detect the model object is attacking
if self.model.isattacking():
# Create animation if needed
if self.attacking_animation is None:
self.attacking_animation = iter(self.attacking_ressource)
# Set image
self.image = next(self.attacking_animation, self.default_ressource)
else:
# Reset animation
self.attacking_animation = None
self.image = self.default_ressource
The question is, how do the model know that it's no longer attacking?
The view could notify the model when the animation is over, but it guess it's not how the MVC pattern is supposed to work. Or, an animation counter could be set in the model, but it doesn't seem right either.
I think you got it the wrong way round.
The attack should not last as long as the animation, but the animation should last as long as the attack.
So the ViewObject is fine as it already asks the model if it is still attacking.
As for the ModelObject, it's up to you how long the attack should last and how to keep track of time. You could for example call pygame.time.get_ticks() to get the number of millisconds since you started your game once in attack, and then periodically check it again, like:
class ModelObject:
def attack():
if not self.attacking:
self.attacking = True
self.started = pygame.time.get_ticks()
# Then compute some stuff
def isattacking():
return self.attacking
def update():
# attack lasts 1000ms
self.attacking &= pygame.time.get_ticks() - self.started < 1000

Object not behaving correctly

I'm using Livewires and pygame and one of my objects in the game that gives you extra lives is being mistaken as an asteroid object, and when the extra lives objects collides with the player it returns the 'Extra lives object has no attribute handle_caught' error message, so can I please have some help.
class Extralives(games.Sprite):
global lives
image = games.load_image('lives.png', transparent = True)
speed = 2
def __init__(self,x,y = 10):
""" Initialize a asteroid object. """
super(Extralives, self).__init__(image = Extralives.image,
x = x, y = y,
dy = Extralives.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
self.add_extralives
def add_extralives(self):
lives+=1
The asteroid class:
class Asteroid(games.Sprite):
global lives
global score
"""
A asteroid which falls through space.
"""
image = games.load_image("asteroid_med.bmp")
speed = 1.7
def __init__(self, x,image, y = 10):
""" Initialize a asteroid object. """
super(Asteroid, self).__init__(image = image,
x = x, y = y,
dy = Asteroid.speed)
def update(self):
""" Check if bottom edge has reached screen bottom. """
if self.bottom>games.screen.height:
self.destroy()
score.value+=10
def handle_caught(self):
if lives.value>0:
lives.value-=1
self.destroy_asteroid()
if lives.value <= 0:
self.destroy_asteroid()
self.end_game()
def destroy_asteroid(self):
self.destroy()
part of the player class which handles the collisions:
def update(self):
""" uses A and D keys to move the ship """
if games.keyboard.is_pressed(games.K_a):
self.x-=4
if games.keyboard.is_pressed(games.K_d):
self.x+=4
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
self.check_collison()
def ship_destroy(self):
self.destroy()
def check_collison(self):
""" Check if catch pizzas. """
global lives
for asteroid in self.overlapping_sprites:
asteroid.handle_caught()
if lives.value <=0:
self.ship_destroy()
for extralives in self.overlapping_sprites:
extralives.add_extralives()
Here is your problem:
for asteroid in self.overlapping_sprites:
asteroid.handle_caught()
if lives.value <=0:
self.ship_destroy()
The fact that you call your loop variable asteroid does not mean that it's magically only going to ever be an asteroid. Not if you have other kinds of objects you can collide with! overlapping_sprites is all overlapping sprites, not just asteroids. At some point asteroid is an ExtraLives object. When you try to call handle_caught() on it, this obviously fails because ExtraLives doesn't have a handle_caught() method.
The simplest solution here is to rename add_extralives to handle_caught on your ExtraLives class. After all, you're doing the same thing: handling the situation where you collide with (or "catch") the object, it's just a different kind of object so the result needs to be different, which you specify by providing different code. Being able to implement entirely different kinds of behavior by calling the same methods (called "polymorphism") is kinda the whole point of object-oriented programming.
The following loop has a similar problem, in that you're calling add_extralives() on objects that might not be of type ExtraLives. Fortunately you can remove this code since you're already handling this situation by renaming add_extralives to handle_caught.
for extralives in self.overlapping_sprites:
extralives.add_extralives()

Categories