0

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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Orange1861
  • 595
  • 1
  • 4
  • 10
  • You have to make sure your code is correct. Currently, there are many indention errors making it very hard to read and/or test your code. – Ted Klein Bergman Aug 13 '16 at 15:53
  • Yes my code is correct, the indentation issues are because of the snippet moving it oddly. – Orange1861 Aug 13 '16 at 15:55
  • You have to edit your code in your question so we can see how the code should be, otherwise it makes it very hard or even impossible to determine what's wrong with the code. – Ted Klein Bergman Aug 13 '16 at 15:58
  • I have fixed the formatting. – Orange1861 Aug 13 '16 at 16:01
  • Maybe you should clarify that this is a class and show us the code where you're blitting the box and where you're calling those functions. I'd like to test your code but have to go, but it might make it easier for others to answer. Otherwise I'll do my best looking at it tomorrow night. – Ted Klein Bergman Aug 13 '16 at 16:08
  • I added everything required for a testing. – Orange1861 Aug 13 '16 at 16:35

1 Answers1

1

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.
Community
  • 1
  • 1
Ted Klein Bergman
  • 9,146
  • 4
  • 29
  • 50
  • Thank you for putting effort into this. This has been a great help, One final question, I don't understand the second remark, could you further explain it? – Orange1861 Aug 15 '16 at 11:17
  • Create a file called *Test* that contains `print("Hello")`. Create another file where you import *Test*. Just importing *Test* will print *"Hello"*. Everything under `if __name__ == "__main__":` will not be run when you import that file however. It's hard to explain in a limited space as the comment section but I believe this video will explain somewhat decent: https://www.youtube.com/watch?v=sugvnHA7ElY. – Ted Klein Bergman Aug 15 '16 at 11:35
  • In your case, when you're importing *Test_Class* it'll execute the code like `game_display = pygame.display.set_mode((display_width, display_height))` and `pygame.display.set_caption("WW1 game")`, which is unnecessary since you're doing exactly the same in your main file. – Ted Klein Bergman Aug 15 '16 at 11:36