Orange1861
Orange1861

Reputation: 595

Odd click issues (Python & Pygame)

I'm creating a program that makes a box move when I select it (By clicking) then clicking again where it needs to go. But this doesn't work. Instead the object moves a few pixels then stutters.

import pygame
import sys
import time
import Test_Class
from pygame import gfxdraw

pygame.init()

black = (1, 1, 1)
white = (255, 255, 255)
red = (255, 0, 0)
orange = (255, 130, 28)
light_orange = (255, 139, 36)
yellow = (231, 231, 42)
green2 = (0, 130, 15)
green = (0, 255, 0)
cyan = (60, 142, 176)
light_blue = (165, 165, 255)
blue = (0, 0, 255)
grey = (127, 127, 127)
light_grey = (191, 191, 191)
purple = (140, 57, 188)
brown = (112, 68, 37)
pink = (237, 167, 203)
dark_grey = (64, 64, 64)

display_width = 1260
display_height = 900

Bob = Test_Class.Army(400, 600)
game_display = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("WW1 game")
clock = pygame.time.Clock()

def game_loop():
    game_exit = False
    while not game_exit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_exit = True
                pygame.quit()
                quit()
                sys.exit()
        game_display.fill(light_grey)
        Bob.shape()
        Bob.selection1()
        Bob.movement()
        pygame.display.update()
        clock.tick(60)

game_loop()
pygame.quit()
quit()
sys.exit()

import pygame
import sys
import time
from pygame import gfxdraw

pygame.init()

black = (1, 1, 1)
white = (255, 255, 255)
red = (255, 0, 0)
orange = (255, 130, 28)
light_orange = (255, 139, 36)
yellow = (231, 231, 42)
green2 = (0, 130, 15)
green = (0, 255, 0)
cyan = (60, 142, 176)
light_blue = (165, 165, 255)
blue = (0, 0, 255)
grey = (127, 127, 127)
light_grey = (191, 191, 191)
purple = (140, 57, 188)
brown = (112, 68, 37)
pink = (237, 167, 203)
dark_grey = (64, 64, 64)

display_width = 1260
display_height = 900

game_display = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("WW1 game")
clock = pygame.time.Clock()

class Army:
    def __init__(self, posx, posy):
        self.posx = posx
        self.posy = posy
        self.movex = posx
        self.object = []
        self.movey = posy
        self.select = False

    def shape(self):
        gfxdraw.box(game_display, (self.posx, self.posy, 48, 30), black)
        gfxdraw.box(game_display, (self.posx + 2, self.posy + 2, 43, 26), blue)
        pygame.draw.line(game_display, black, (self.posx, self.posy + 1),
                         (self.posx + 47, self.posy + 28), 3)

    def movement(self):
        if self.movex > self.posx:
            self.posx += 2
        elif self.movex < self.posx:
            self.posx -= 2
        if self.movey > self.posy:
            self.posy += 2
        elif self.movey < self.posy:
            self.posy -= 2

    def selection1(self):
        mouse = pygame.mouse.get_pos()
        if self.posx + 48 > mouse[0] > self.posx and self.posy + 30 > mouse[1] > self.posy:
            click = pygame.mouse.get_pressed()
            if click[0] == 1:
                gfxdraw.box(game_display, (self.posx + 2, self.posy + 2, 43, 26), light_blue)
                pygame.draw.line(game_display, dark_grey, (self.posx, self.posy + 1), (self.posx + 47, self.posy + 28), 3)
                self.select = True
        while self.select is True:
            click = pygame.mouse.get_pressed()
            if click[0] == 1:
                print("True")
                mouse = pygame.mouse.get_pos()
                self.movex = mouse[0]
                self.movey = mouse[1]
                self.select = False
                pygame.display.update()
                clock.tick(60)

Edit: Added everything required to test this.

Upvotes: 0

Views: 44

Answers (1)

Ted Klein Bergman
Ted Klein Bergman

Reputation: 9756

The stutter

The stutter happens if you click somewhere that's not divisible by 2, since you're tank is moving by 2 pixels each loop. So if you're clicking at x = 201 and your tank is at x = 200 it'll move to 202, and then back to 200 and repeat. Same is true for y.

A solution to the problem could be to add 1 to x or y if they're are odd. In other words: make it so the user's selected position only is in even numbers. Another solution could be to add the distance between for example posx and movex if the current distance is less than the amount of pixels the tank is moving. Like this:

def movement(self):
    if self.movex > self.posx:
        self.posx += 2 if abs(self.posx - self.movex) > 2 else abs(self.posx - self.movex)
    elif self.movex < self.posx:
        self.posx -= 2 if abs(self.posx - self.movex) > 2 else abs(self.posx - self.movex)
    if self.movey > self.posy:
        self.posy += 2 if abs(self.posy - self.movey) > 2 else abs(self.posy - self.movey)
    elif self.movey < self.posy:
        self.posy -= 2 if abs(self.posy - self.movey) > 2 else abs(self.posy - self.movey)

Selecting and moving

The problem is that you're checking if the mouse button is down, rather than if the mouse button is clicked. This means that if you're holding down the mouse button for longer than 1/60 ≈ 0.017 seconds, you're both selecting and moving the tank.

A solution could be to create two methods, select_tank and select_pos, which could be called in the event loop when the user presses the mouse button. I split your method selection1 and took away som unnecessary code. Also, notice that I put a line of code in draw to make it change color depending on whether it's selected or not.

class Army(object):
    def __init__(self, posx, posy):
        self.posx = posx
        self.posy = posy
        self.movex = posx
        self.movey = posy
        self.select = False

    def draw(self):
        color = blue if not self.select else light_blue
        gfxdraw.box(game_display, (self.posx, self.posy, 48, 30), black)
        gfxdraw.box(game_display, (self.posx + 2, self.posy + 2, 43, 26), color)
        pygame.draw.line(game_display, black, (self.posx, self.posy + 1),(self.posx + 47, self.posy + 28), 3)

    def move(self):
        if self.movex > self.posx:
            self.posx += 2 if abs(self.posx - self.movex) > 2 else abs(self.posx - self.movex)
        elif self.movex < self.posx:
            self.posx -= 2 if abs(self.posx - self.movex) > 2 else abs(self.posx - self.movex)
        if self.movey > self.posy:
            self.posy += 2 if abs(self.posy - self.movey) > 2 else abs(self.posy - self.movey)
        elif self.movey < self.posy:
            self.posy -= 2 if abs(self.posy - self.movey) > 2 else abs(self.posy - self.movey)

    def select_tank(self):
        mouse = pygame.mouse.get_pos()
        if self.posx + 48 > mouse[0] > self.posx and self.posy + 30 > mouse[1] > self.posy:
            self.select = True

    def select_pos(self):
        mouse = pygame.mouse.get_pos()
        self.movex = mouse[0]
        self.movey = mouse[1]
        self.select = False


def game_loop():
    game_exit = False
    while not game_exit:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_exit = True
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if bob.select:
                    bob.select_pos()
                else:
                    bob.select_tank()
        game_display.fill(light_grey)
        bob.move()
        bob.draw()
        pygame.display.update()
        clock.tick(60)

Remarks

  • Use just lower case letters and underscores for your variable names to don't get others confused whether it's a class or variable.
  • If you're importing from your own classes, make sure you don't have code that's being run unnecessary. In your Test_Class you set the display mode and captions when it's being imported. This is when you could use if __name__ == "__main__":.
  • Don't repeat unnecessary code. In your code you're using 4 functions that terminates, when you only need 1. In selection1 you're updating the screen and calling clock.tick method even if you're doing it in the game loop.
  • When asking questions on Stack overflow, make sure to give the readers Minimal, Complete, and Verifiable code. You could have removed most of the color constants, import time (since you're not using it) and self.object in the Army class (also unused). It would make the code a bit shorter and thus slightly easier to read.

Upvotes: 1

Related Questions