0

I'm new to programming and would appreciate guidance/ feedback.

Below is a full working script:

I've managed to get the player sprite to be controlled by WASD, the asteroid sprite is also now rendered on the screen, with some physics to move it around. It should rebound off walls too but doesn't. But for some reason the update function isn't correctly calling on the Asteroid class, I believe - unless something else is wrong with it.

Greatly appreciate all help so ar and future guidance!

import arcade
import random

""" Universal variables """

SPRITE_SCALING_PLAYER = 0.5
SPRITE_SCALING_ASTEROID = 0.35

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

MOVEMENT_SPEED = 5

class Player(arcade.Sprite):
    # PLAYER
    def update(self):
        # COLLISION
        self.center_x += self.change_x
        self.center_y += self.change_y

        if self.left < 0:
            self.left = 0
        elif self.right > SCREEN_WIDTH - 1:
            self.right = SCREEN_WIDTH - 1

        if self.bottom < 0:
            self.bottom = 0
        elif self.top > SCREEN_HEIGHT - 1:
            self.top = SCREEN_HEIGHT - 1

class Asteroid(arcade.Sprite):
    # ASTEROID
    def __init__(self, filename, sprite_scaling):

        super().__init__(filename, sprite_scaling)

        self.change_x = 0
        self.change_y = 0

    def update(self):

        # Move the asteroid
        self.center_x += self.change_x
        self.center_y += self.change_y

        # rebound
        if self.left < 0:
            self.change_x *= -1

        if self.right > SCREEN_WIDTH:
            self.change_x *= -1

        if self.bottom < 0:
            self.change_y *= -1

        if self.top > SCREEN_HEIGHT:
            self.change_y *= -1

# MAIN GAME CLASS
class MyGame(arcade.Window):
    """ Our custom Window Class"""

    def __init__(self):
        """ Initializer """
        # Call the parent class initializer
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Alien")

        # Background image will be stored in this variable
        self.background = ("space_bg.png")

        # Variables that will hold sprite lists
        self.all_sprite_list = ["ufo_sprite.png", "asteroid.gif"]

        # Set up player
        self.player_sprite = self.all_sprite_list[0]

        # Set up asteroid
        self.asteroid_sprite = self.all_sprite_list[1]

        # Don't show the mouse cursor
        self.set_mouse_visible(False)

        # arcade.set_background_color(arcade.color.BLACK)

    def setup(self):
        """ Set up the game and initialize the variables. """
        # background
        self.background = arcade.load_texture(self.background)

        # Sprite lists
        self.all_sprite_list = arcade.SpriteList()

        # Set up the player
        self.player_sprite = Player("ufo_sprite.png", SPRITE_SCALING_PLAYER)
        self.player_sprite.center_x = (SCREEN_WIDTH * 0.50)
        self.player_sprite.center_y = (SCREEN_HEIGHT * 0.50)
        self.all_sprite_list.append(self.player_sprite)

        # Set up asteroid
        self.asteroid_sprite = Asteroid("asteroid.gif", SPRITE_SCALING_ASTEROID)
        Asteroid.center_x = random.randrange(SCREEN_WIDTH)
        Asteroid.center_y = random.randrange(SCREEN_HEIGHT)
        Asteroid.change_x = random.randrange(-4, 4)
        Asteroid.change_y = random.randrange(-4, 4)
        self.all_sprite_list.append(self.asteroid_sprite)

    def on_draw(self):
        # needed before other drawn elements
        arcade.start_render()

        # draw background
        arcade.draw_texture_rectangle(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2,
                                      SCREEN_WIDTH, SCREEN_HEIGHT, self.background)
        # draw sprites
        self.all_sprite_list.draw()

    def update(self, delta_time):
        """ Movement and game logic """

        self.all_sprite_list.update()


    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed. """

        if key == arcade.key.W:
            self.player_sprite.change_y = MOVEMENT_SPEED
        elif key == arcade.key.S:
            self.player_sprite.change_y = -MOVEMENT_SPEED
        elif key == arcade.key.A:
            self.player_sprite.change_x = -MOVEMENT_SPEED
        elif key == arcade.key.D:
            self.player_sprite.change_x = MOVEMENT_SPEED
    #    elif key == arcade.key.SPACE:
    #        self.player_sprite.change_x = MOVEMENT_SPEED

    def on_key_release(self, key, modifiers):
        """Called when the user releases a key. """

        if key == arcade.key.W or key == arcade.key.S:
            self.player_sprite.change_y = 0
        elif key == arcade.key.A or key == arcade.key.D:
            self.player_sprite.change_x = 0
    #    elif key == arcade.key.SPACE:
    #        self.player_sprite.change_y = (SCREEN_HEIGHT * 0.005)


def main():
    """ Main method """
    window = MyGame()
    window.setup()
    arcade.run()

    arcade.schedule(update, 1 / 80)

if __name__ == "__main__":
    main()
Alderven
  • 7,569
  • 5
  • 26
  • 38
qdqshred
  • 33
  • 7

1 Answers1

1

Could you please tell us the exact problem (exception, sprites not moving as expected,...) However, the code you provided gave me the following ideas:

  • calling the constructor of the inherited class

    class Asteroid(arcade.Sprite):
        def __init__(self):
            super(Asteroid, self).__init__()
            # or
            arcade.Sprite.__init__(self)
    

    more here (it's odd but I found nothing about calling the base constructor under the classes-inheritance-section in the official docs, maybe someone can provide something

  • using variables which don't exist or are created below

    self.center_x += self.change_x * delta_time
    self.center_y += self.change_y * delta_time
    

    self.change_x is created below (so it does not exist at that time) and delta_time isn't anywhere else (maybe it's just an incomplete snippet?)

  • Do you really want to create an instance of Sprite after you made your own child class of Sprite (Asteroid)

    self.asteroid_sprite = Asteroid("asteroid.gif", SPRITE_SCALING_ASTEROID)
    

    instead of

    self.asteroid_sprite = arcade.Sprite("asteroid.gif", SPRITE_SCALING_ASTEROID)
    

EDIT: try to do this before assigning the variables in the Asteroid constructor:

def __init__(self, *args, **kwargs):
    super(Asteroid, self).__init__(*args, **kwargs)

args and kwargs are placeholders for the things you are passing in below like the imagepath

r0w
  • 155
  • 1
  • 13
  • Thanks so much for such an informative response. In retrospect, I appreciate I was offering far too little context previously. Your comments gave me a lot to think about. In particular, as you rightly guessed, I was calling upon a variable that had not yet been defined. I've tried all your suggestions but am still at a loss. I edited the OP with functioning code to better contextualize my pain! Nevertheless, I would very much appreciate a further review. Appreciate it! – qdqshred Oct 23 '18 at 17:36
  • @qdqshred I should probably admit that I never worked with `arcade` before (only with `pyglet`, which has some similarities though) so I might not spot the problem if it is directly tied to the usage that module. But I will try to look into the code from your edit. – r0w Oct 23 '18 at 18:38
  • And I have to add to the bad news: apparently, does `arcade` only run on 3.6 and above and my pc resists to update from 3.4 for a couple of months now which means I'm not able to run your code – r0w Oct 23 '18 at 19:21
  • 1
    However, are you aware that you don't use your own `Player` class? Maybe try `self.player_sprite = Player("ufo_sprite.png", SPRITE_SCALING_PLAYER)` in `MyGame.setup` – r0w Oct 23 '18 at 19:27
  • Oh wow! Thanks! So yes, I completely forgot about the collision code I'd put up there, was so focused on the asteroid I'd not noticed player collision wasn't also a problem. The adjustment you provided worked wonderfully, and with that; the player could no longer leave the window! Using all your suggestions, and (what I think I may have learnt about OOP/ classes), I've edited the OP with another code sample - it's still not functioning as intended. I decided to put the Asteroid's function within a class method to avoid the prior issue of calling on code defined later in the script. – qdqshred Oct 23 '18 at 20:13
  • and also call correctly on the class in the myGame setup.line 94, in setup self.asteroid_sprite = Asteroid("asteroid.gif", SPRITE_SCALING_ASTEROID) TypeError: __init__() takes 1 positional argument but 3 were given is the error I get. I feel I may still be missing some fundamental concept here :S – qdqshred Oct 23 '18 at 20:14
  • Can you please tell me what part is not working correctly now because I can't run the code – r0w Oct 23 '18 at 20:34
  • I know only that the error is probably being caused by how I handle Asteroid as a class or it's method. All the physics and functions work as intended (thanks to your help) for the player sprite, but when I try to add a similar function to the Asteroid class, with physics that appear to make sense, it causes the program to cease to run entirely. I was able to get all sprites rendered on screen as intended as well as the physics implemented correctly through Player's class method. – qdqshred Oct 23 '18 at 21:02
  • I edited something to my answer which could get rid of the `TypeError` you mentioned above – r0w Oct 23 '18 at 21:20
  • After trying the edited code the error I got was: line 346, in call_scheduled_functions item.func(now - item.last_ts, *item.args, **item.kwargs) File "FILEPATH", line 112, in update self.all_sprite_list.update() File "FILEPATH" line 340, in update sprite.update() File "FILEPATH", line 42, in update self.x += self.change_x * delta_time NameError: name 'delta_time' is not defined Thanks so much for your continued efforts. Is it because even though delta_time is now in a class method and is called in update, it's still not defined until then? – qdqshred Oct 23 '18 at 21:34
  • The problem I guess is that you have `delta_time` in `MyGame.update` but not in `Asteroid.update` at least in the last code, you provided here. Same problem with `update` in `update.delta_x *= -1`. With `delta_time` I know at least what it is and where it's coming from but I have no clue where the `update` variable inside the update method of Asteroid variable is coming from – r0w Oct 23 '18 at 21:45
  • So I moved the movement logic containing delta_time to the update function which does call on delta_time, replacing self with Asteroid. but this is still resulting in errors. TypeError: __init__() takes 1 positional argument but 3 were given And, you mean the method for update within the arcade library? – qdqshred Oct 23 '18 at 22:07
  • In the `Asteroid` class in `update` after the first if-statement `update.delta_x *= -1` is that `self` instead of `update`? – r0w Oct 23 '18 at 22:11
  • I heavily update the OP after also heavily updating the code. The physics are simpler and function (mostly) but I'm still having issues accessing the class. – qdqshred Oct 24 '18 at 18:08