ผลต่างระหว่างรุ่นของ "Prg2/arcade5 maze"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
แถว 9: แถว 9:
  
 
== Pacman ==
 
== Pacman ==
 +
=== Create a new project and set up a Git repository ===
 +
We will start with an empty game template.  Put the following code in our main program <tt>maze.py</tt>.
 +
 +
{{synfile|maze.py}}
 +
<syntaxhighlight lang="python">
 +
import arcade
 +
 +
SCREEN_WIDTH = 800
 +
SCREEN_HEIGHT = 600
 +
 +
class FlappyDotWindow(arcade.Window):
 +
    def __init__(self, width, height):
 +
        super().__init__(width, height)
 +
 +
        arcade.set_background_color(arcade.color.WHITE)
 +
 +
    def on_draw(self):
 +
        arcade.start_render()
 +
       
 +
 +
def main():
 +
    window = FlappyDotWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
 +
    arcade.set_window(window)
 +
    arcade.run()
 +
 +
if __name__ == '__main__':
 +
    main()
 +
</syntaxhighlight>
 +
 +
Try to run the game to see if an empty white window appears.
 +
Then, create a git repository at the project directory and commit the code.
 +
 +
{{gitcomment|Create your git repository.}}
 +
 +
=== Creating the player model and the sprite ===
 +
In this step, we shall create a sprite for the player, and show it in the middle of the screen.
 +
 +
Use a graphic editor to create an image for our player.  The image should be of size 40 pixels x 40 pixels.  Save the image as <tt>images/dot.png</tt> and try to make it look cute.
 +
 +
We will continue our model/window code structure.  So let's create a dot model (called <tt>Player</tt>) and <tt>World</tt> in <tt>models.py</tt> as in our previous projects.  Note that currently the Player do nothing in <tt>update</tt>
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    def __init__(self, world, x, y):
 +
        self.world = world
 +
        self.x = x
 +
        self.y = y
 +
 +
    def update(self, delta):
 +
        pass
 +
 +
class World:
 +
    def __init__(self, width, height):
 +
        self.width = width
 +
        self.height = height
 +
 +
        self.player = Player(self, width // 2, height // 2)
 +
 +
    def update(self, delta):
 +
        self.player.update(delta)
 +
</syntaxhighlight>
 +
 +
As in the previous projects, we then use <tt>ModelSprite</tt> to display the sprite.  Add the class in <tt>flappy.py</tt> and update <tt>FlappyDotWindow</tt> accordingly.
 +
 +
{{synfile|flappy.py}}
 +
<syntaxhighlight lang="python">
 +
class ModelSprite(arcade.Sprite):
 +
    def __init__(self, *args, **kwargs):
 +
        self.model = kwargs.pop('model', None)
 +
 +
        super().__init__(*args, **kwargs)
 +
 +
    def sync_with_model(self):
 +
        if self.model:
 +
            self.set_position(self.model.x, self.model.y)
 +
 +
    def draw(self):
 +
        self.sync_with_model()
 +
        super().draw()
 +
 +
 +
class FlappyDotWindow(arcade.Window):
 +
    def __init__(self, width, height):
 +
        super().__init__(width, height)
 +
 +
        arcade.set_background_color(arcade.color.WHITE)
 +
 +
        self.world = World(SCREEN_WIDTH, SCREEN_HEIGHT)
 +
       
 +
        self.dot_sprite = ModelSprite('images/dot.png', model=self.world.player)
 +
 +
    def update(self, delta):
 +
        self.world.update(delta)
 +
 +
    def on_draw(self):
 +
        arcade.start_render()
 +
 +
        self.dot_sprite.draw()
 +
</syntaxhighlight>
 +
 +
Try to run the game.  You should see your sprite in the middle of the screen.
 +
 +
{{gitcomment|Commit your work.}}
 +
 +
=== Review of physics ===
 +
You might forget all these, but if you want objects in your game to look and act a bit like real objects, you might have to recall stuffs you learned from mechanics.
 +
 +
Let's look at the basics.  An object has a position, its position changes if it has non-zero velocity.
 +
 +
How can you change the player's position?  We can set its <tt>x</tt> and <tt>y</tt> attribute on the model.
 +
 +
If you want to apply the velocity, you can change the player position based on the velocity.
 +
 +
If there is an acceleration, the object's velocity also changes.  The <tt>Player</tt> currently does not have <tt>velocity</tt> as its attribute, so we will add it.  Now, you can update the velocity based on the acceleration.
 +
 +
These attributes (the position, the velocity, and the acceleration) all have directions.  Sometimes, you see negative velocity; this means the object is moving in an opposite direction as the positive direction.  We shall follow the standard co-ordinate system for ''arcade'', i.e., for the y-axis, we think of the direction as going upwards.
 +
 +
While in Physics, everything is continuous, but when writing games, we don't really need exact physics, so we can move objects in discrete steps.  (In fact, method <tt>update</tt> is also called with parameter <tt>delta</tt>, the time period between this call and the last call, and you can use this to make your simulation more smooth.)
 +
 +
So the usual pseudo code for physics is as follows.
 +
 +
pos = pos + velocity;
 +
velocity = velocity + acceleration
 +
 +
=== Falling dot ===
 +
To simulate the player falls, we should maintain the player's current velocity, so that we can make it falls as close as the real object. 
 +
 +
Let's add this line that initialize property <tt>vy</tt> in <tt>Player</tt>'s initialization code:
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    def __init__(self, world, x, y):
 +
        # ... [old code hidden]
 +
 +
        self.vy = 15
 +
</syntaxhighlight>
 +
 +
You may wonder why we put 15 here.  It is just pure guess at this point.  However, when you write games, you might want to try various possible values and pick the best one (i.e., the one that make the game fun).
 +
 +
The <tt>update</tt> method changes the player's position
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    # ...
 +
    def update(self, delta):
 +
        self.y += self.vy
 +
        self.vy -= 1
 +
</syntaxhighlight>
 +
 +
Note that we update <tt>self.vy</tt> at the end of update.  The constant <tt>-1</tt> is the acceleration.  The parameter <tt>delta</tt> represents delta time; we do not use it for now.
 +
 +
Try to run the program.  You should see the player falling.
 +
 +
While our program works, don't just rush to commit right away.  Let's try to get rid of the magic numbers first, by defining them explicitly.
 +
 +
Add these contants at the beginning of <tt>Player</tt> in <tt>models.py</tt>.  These are '''class variables'''.
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    GRAVITY = 1
 +
    STARTING_VELOCITY = 15
 +
 +
    # ...
 +
</syntaxhighlight>
 +
 +
Then replace <tt>15</tt> and <tt>1</tt> in the code with the appropriate constants as follows.  Note that we can refer to these variables as <tt>self.GRAVITY</tt> or <tt>Player.GRAVITY</tt>.
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    # ...
 +
    def __init__(self, world, x, y):
 +
        # ...
 +
 +
        self.vy = Player.STARTING_VELOCITY
 +
 +
    def update(self, delta):
 +
        # ...
 +
        self.vy -= Player.GRAVITY
 +
</syntaxhighlight>
 +
 +
{{gitcomment|When your program looks good, commit it}}
 +
 +
=== Jumping dot ===
 +
 +
Now, let's make the dot jumps.  Let's add method <tt>jump</tt> that set the velocity to some positive amount.  Let's create a constant <tt>JUMPING_VELOCITY</tt> to represent this magic number as well.
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class Player:
 +
    # ...
 +
    JUMPING_VELOCITY = 15
 +
 +
    # ...
 +
 +
    def jump(self):
 +
        self.vy = Player.JUMPING_VELOCITY
 +
</syntaxhighlight>
 +
 +
To jump, we have to call <tt>player.jump()</tt> in an appropriate time.  We will response to keyboard inputs.  We shall follow the style we did in the last tutorial.
 +
 +
First, add method <tt>on_key_press</tt> in <tt>FlappyDotWindow</tt> to forward the call to the world.
 +
 +
{{synfile|flappy.py}}
 +
<syntaxhighlight lang="python">
 +
class FlappyDotWindow(arcade.Window):
 +
    # ...
 +
 +
    def on_key_press(self, key, key_modifiers):
 +
        self.world.on_key_press(key, key_modifiers)
 +
</syntaxhighlight>
 +
 +
Then, in <tt>World</tt> for any key pressed, call <tt>jump</tt>.
 +
 +
{{synfile|models.py}}
 +
<syntaxhighlight lang="python">
 +
class World:
 +
    # ...
 +
 +
    def on_key_press(self, key, key_modifiers):
 +
        self.player.jump()
 +
</syntaxhighlight>
 +
 +
To test this increment, you will have to click on the game canvas, and then quickly hit on any key to get the dot jumping.  Try a few times to see how the dot moves.  You can adjust the jumping velocity to make the movement nice.
 +
 +
{{gitcomment|After a few trials to make sure your code works, please commit.}}
 +
 +
*********************************************************************************
 +
********************************* CHECK POINT 4.1 *******************************
 +
*********************************************************************************

รุ่นแก้ไขเมื่อ 16:18, 28 กุมภาพันธ์ 2562

This is part of the course Programming 2, the material is originally from 01219245/cocos2d/Maze from 01219245.

Rough steps

We will follow these steps to implement a pacman-like game.

  • Shows and moves pacman
  • Shows maze
  • blah

Pacman

Create a new project and set up a Git repository

We will start with an empty game template. Put the following code in our main program maze.py.

File: maze.py
import arcade
 
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
 
class FlappyDotWindow(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
 
        arcade.set_background_color(arcade.color.WHITE)

    def on_draw(self):
        arcade.start_render()
        
 
def main():
    window = FlappyDotWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
    arcade.set_window(window)
    arcade.run()
 
if __name__ == '__main__':
    main()

Try to run the game to see if an empty white window appears. Then, create a git repository at the project directory and commit the code.

Gitmark.png Create your git repository.

Creating the player model and the sprite

In this step, we shall create a sprite for the player, and show it in the middle of the screen.

Use a graphic editor to create an image for our player. The image should be of size 40 pixels x 40 pixels. Save the image as images/dot.png and try to make it look cute.

We will continue our model/window code structure. So let's create a dot model (called Player) and World in models.py as in our previous projects. Note that currently the Player do nothing in update

File: models.py
class Player:
    def __init__(self, world, x, y):
        self.world = world
        self.x = x
        self.y = y

    def update(self, delta):
        pass

class World:
    def __init__(self, width, height):
        self.width = width
        self.height = height
 
        self.player = Player(self, width // 2, height // 2)
 
     def update(self, delta):
        self.player.update(delta)

As in the previous projects, we then use ModelSprite to display the sprite. Add the class in flappy.py and update FlappyDotWindow accordingly.

File: flappy.py
class ModelSprite(arcade.Sprite):
    def __init__(self, *args, **kwargs):
        self.model = kwargs.pop('model', None)
 
        super().__init__(*args, **kwargs)
 
    def sync_with_model(self):
        if self.model:
            self.set_position(self.model.x, self.model.y)
 
    def draw(self):
        self.sync_with_model()
        super().draw()


class FlappyDotWindow(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)

        arcade.set_background_color(arcade.color.WHITE)

        self.world = World(SCREEN_WIDTH, SCREEN_HEIGHT)
        
        self.dot_sprite = ModelSprite('images/dot.png', model=self.world.player)

    def update(self, delta):
        self.world.update(delta)

    def on_draw(self):
        arcade.start_render()

        self.dot_sprite.draw()

Try to run the game. You should see your sprite in the middle of the screen.

Gitmark.png Commit your work.

Review of physics

You might forget all these, but if you want objects in your game to look and act a bit like real objects, you might have to recall stuffs you learned from mechanics.

Let's look at the basics. An object has a position, its position changes if it has non-zero velocity.

How can you change the player's position? We can set its x and y attribute on the model.

If you want to apply the velocity, you can change the player position based on the velocity.

If there is an acceleration, the object's velocity also changes. The Player currently does not have velocity as its attribute, so we will add it. Now, you can update the velocity based on the acceleration.

These attributes (the position, the velocity, and the acceleration) all have directions. Sometimes, you see negative velocity; this means the object is moving in an opposite direction as the positive direction. We shall follow the standard co-ordinate system for arcade, i.e., for the y-axis, we think of the direction as going upwards.

While in Physics, everything is continuous, but when writing games, we don't really need exact physics, so we can move objects in discrete steps. (In fact, method update is also called with parameter delta, the time period between this call and the last call, and you can use this to make your simulation more smooth.)

So the usual pseudo code for physics is as follows.

pos = pos + velocity;
velocity = velocity + acceleration

Falling dot

To simulate the player falls, we should maintain the player's current velocity, so that we can make it falls as close as the real object.

Let's add this line that initialize property vy in Player's initialization code:

File: models.py
class Player:
    def __init__(self, world, x, y):
        # ... [old code hidden]

        self.vy = 15

You may wonder why we put 15 here. It is just pure guess at this point. However, when you write games, you might want to try various possible values and pick the best one (i.e., the one that make the game fun).

The update method changes the player's position

File: models.py
class Player:
    # ...
    def update(self, delta):
        self.y += self.vy
        self.vy -= 1

Note that we update self.vy at the end of update. The constant -1 is the acceleration. The parameter delta represents delta time; we do not use it for now.

Try to run the program. You should see the player falling.

While our program works, don't just rush to commit right away. Let's try to get rid of the magic numbers first, by defining them explicitly.

Add these contants at the beginning of Player in models.py. These are class variables.

File: models.py
class Player:
    GRAVITY = 1
    STARTING_VELOCITY = 15

    # ...

Then replace 15 and 1 in the code with the appropriate constants as follows. Note that we can refer to these variables as self.GRAVITY or Player.GRAVITY.

File: models.py
class Player:
    # ...
    def __init__(self, world, x, y):
        # ... 

        self.vy = Player.STARTING_VELOCITY

    def update(self, delta):
        # ...
        self.vy -= Player.GRAVITY
Gitmark.png When your program looks good, commit it

Jumping dot

Now, let's make the dot jumps. Let's add method jump that set the velocity to some positive amount. Let's create a constant JUMPING_VELOCITY to represent this magic number as well.

File: models.py
class Player:
    # ...
    JUMPING_VELOCITY = 15

    # ...

    def jump(self):
        self.vy = Player.JUMPING_VELOCITY

To jump, we have to call player.jump() in an appropriate time. We will response to keyboard inputs. We shall follow the style we did in the last tutorial.

First, add method on_key_press in FlappyDotWindow to forward the call to the world.

File: flappy.py
class FlappyDotWindow(arcade.Window):
    # ...

    def on_key_press(self, key, key_modifiers):
         self.world.on_key_press(key, key_modifiers)

Then, in World for any key pressed, call jump.

File: models.py
class World:
    # ...

    def on_key_press(self, key, key_modifiers):
        self.player.jump()

To test this increment, you will have to click on the game canvas, and then quickly hit on any key to get the dot jumping. Try a few times to see how the dot moves. You can adjust the jumping velocity to make the movement nice.

Gitmark.png After a few trials to make sure your code works, please commit.
*********************************************************************************
********************************* CHECK POINT 4.1 *******************************
*********************************************************************************