Mouse in Python and Cockroach

Introduction

This is the tenth part of a multi-part set of articles for setting up a zombie shooter video game. In this session we will learn to read the mouse in Python with Cockroach used for managing GUI objects and other data like game scores.

In this lesson we will explore how to get mouse input from the player with event handler methods built into the Python Arcade framework specifically for mouse detection. The functions supplied from that library will even reveal the mouse cursor position in terms of x and y coordinates we will use to move the player’s Sprite toward the mouse cursor when the player clicks the right mouse button. We’ll use the left mouse button to fire bullets in that same direction.

First, we’ll briefly look over some of the related preceding parts in this series before building on what we learned there.

Prerequisites

  • Be sure to read part 1 through part nine of this series where with each part we build upon what we learned in the previous ones.

  • Make sure the following libraries are installed on your development/testing machine: arcade, datetime, flask, math, os, sys, psycopg2, pyautogui, random, and datetime. You will see all the code in the full source. As we move through each lesson, you will learn more functions that require these libraries.

  • IMPORTANT: All source code, image files, and sound files are available here. For each lesson we copied only the necessary parts of that source code into the current lesson and analyzing it, explaining how that portion works, so not every lesson will have SQL and/or detailed references to/use of CockroachDB.

  • Arcade documentation. The Python library we used for the game-related classes, methods, and functions in Zombie Feeder. Some of the code here was modified from this awesome open source project: Python Arcade Library

  • Gain a deeper understanding of Arcade here.

Because of how important it is to import the correct libraries, we’ll show that import code here:

Libraries for game

1
2
3
4
5
6
7
import arcade
import math
import random
import sys
import os
import pyautogui
from datetime import datetime, timedelta

Now, because so much of our code relies on certain constants we created, we’ll show those constants here, heavily commented:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# SET UP CONSTANTS
SCREEN_TITLE = "Zombie Feeder"
# Beginning difficulty level, which can be
# changed via the "-" and "+(=)" buttons.
DIFFICULTY = 4
# How many enemies?
ENEMY_COUNT_INITIAL = 8
ENEMY_SPEED = 2 + (DIFFICULTY/12)
# Set the following to true for
# harder game play where enraged
# enemies are immune to obstacles.
ENEMY_ENRAGED_IMMUNE_TO_BLOCKS = True
# How many bounces before an
# enemy becomes immune to blocks?
ENEMY_FRUSTRATION_THRESHOLD = 160
# Allow enemies to "teleport"
# from any edge of the screen
# to opposite edge.
# Set to False for easier, where
# the enemies will "bounce" from
# the screen edge.
ENEMY_SCREEN_EDGE_TRAVERSE = True
# Beginning size of all the Sprites
# on the screen.
SCALE = 0.25
PLAYER_STARTING_LIVES = 4
PLAYER_IMMUNE_TO_BLOCKS = True
MONITOR_RES_WIDTH, MONITOR_RES_HEIGHT = pyautogui.size()
SCREEN_WIDTH = 1920
SCREEN_HEIGHT = 1080
# Make sure SCREEN_WIDTH is not bigger than monitor width.
if SCREEN_WIDTH > MONITOR_RES_WIDTH:
    SCREEN_WIDTH = MONITOR_RES_WIDTH
# Make sure SCREEN_HEIGHT is not bigger than monitor width.
if SCREEN_HEIGHT > MONITOR_RES_HEIGHT:
    SCREEN_HEIGHT = MONITOR_RES_HEIGHT
# Number of obstacles are based on the screen width.
BLOCKS_NUMBER = int(SCREEN_WIDTH/20)
# Set up screen edges.
SPACE_OFFSCREEN = 1
LIMIT_LEFT = -SPACE_OFFSCREEN
LIMIT_RIGHT = SCREEN_WIDTH + SPACE_OFFSCREEN
LIMIT_BOTTOM = -SPACE_OFFSCREEN
LIMIT_TOP = SCREEN_HEIGHT + SPACE_OFFSCREEN
# Draw screens from database.
# If set to False, screens will
# be drawn using the random function
# to place obstacles.
SCREEN_FROM_DATABASE = False

Now for the meat of the lesson: We’ll use a built-in Python Arcade event listener for reading mouse movements and clicks to fire bullets, which in this case are brains being thrown by our Player, represented by a monkey head, at zombies. Note: The following functionality is not implemented in the full game source code yet, which relies on the keyboard for input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# Initialize mouse cursor to be visible:
self.set_mouse_visible(True)

def on_mouse_press(self, x, y, button, modifiers):
    # First:
    # Handle Mouse button right click
    # IF they clicked the right mouse button, they
    # will move toward the mouse cursor.
    # Move the center of the player toward the mouse x, y
    # First figure out direction to travel, based
    # on the mouse cursor's position on the GUI.
    if button == arcade.MOUSE_BUTTON_RIGHT:
        move_x = 0
        move_y = 0
        # We are going to look at the distance between
        # the player and the mouse cursor to determine velocity.
        # In order to do so, we will add up both the vertical
        # distance, represented by y, and the horizontal
        # distance, represented by x.
        i_distance = 0
        if x < self.player_sprite.center_x:
            move_x = -1
            i_distance += self.player_sprite.center_x - x
        if x > self.player_sprite.center_x:
            move_x = 1
            i_distance += x - self.player_sprite.center_x
        if y < self.player_sprite.center_y:
            move_y = -1
            i_distance += self.player_sprite.center_y - y
        if y > self.player_sprite.center_y:
            move_y = 1
            i_distance += y - self.player_sprite.center_y
        self.player_sprite.thrust = 0.05 * i_distance

        # If the player is moving, add gravity-like "drag".
        if self.speed > 0:
            self.speed -= self.drag
            # Make sure player speed is not negative
            # after it was reduced by drag:
            if self.speed < 0:
                self.speed = 0
        if self.speed < 0:
            self.speed += self.drag
            if self.speed > 0:
                self.speed = 0
        self.speed += self.thrust
        # Make sure player speed does
        # not get out of hand.
        if self.speed > self.max_speed:
            self.speed = self.max_speed
        if self.speed < -self.max_speed:
            self.speed = -self.max_speed

        # Homework:
        # Here is where you calculate self.player_sprite.angle
        # based on mouse angle from the player's Sprite.
        # Clues:
        # (1) Use the move_x and move_y we calculated above to calculate angle.
        # (2) Modify the following formulas accordingly:
        # self.change_x = -math.sin(math.radians(self.angle)) * self.speed
        # self.change_y = math.cos(math.radians(self.angle)) * self.speed

    # When the left mouse button is clicked,
    # this functionality is used.
    # Notice we are making sure the player can not
    # fire bullets while still respawning.
    if button == arcade.MOUSE_BUTTON_LEFT and not self.player_sprite.respawning:
        # Initialize the new sprite with a PNG image.
        # It is helpful to use PNG because it allows
        # for image background transparency.
        bullet_sprite = bulletSprite("./resources/images/bullet.png", SCALE)
        # Set the bullet's guid property.
        bullet_sprite.guid = "brain"
        # Assign a speed to the bullet.
        bullet_speed = 7
        # Set a direction for the bullet to fly in.
        bullet_sprite.change_y = \
            math.cos(math.radians(self.player_sprite.angle)) * bullet_speed
        bullet_sprite.change_x = \
            -math.sin(math.radians(self.player_sprite.angle)) \
            * bullet_speed
        # Set location of the bullet.
        bullet_sprite.center_x = self.player_sprite.center_x
        bullet_sprite.center_y = self.player_sprite.center_y
        # Update the bullet sprite in memory.
        bullet_sprite.update()
        # Add the new bullet to the two relevant sprite lists.
        self.list_all_sprites.append(bullet_sprite)
        self.list_bullets.append(bullet_sprite)
        # Play a throwing sound.
        arcade.play_sound(self.sound_throw)

Conclusion

In this tenth part of the multi-article for building a zombie shooter game, we explored using the Arcade library to retrieve input from the mouse and process that input into changes in the position and speed of the player’s Sprite object. In future parts we manage score keeping and learn how to swap out images for any given Sprite to create more diversity of visual effects.

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.