Python Forum
[PyGame] Terrible Sprite controls, need help.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyGame] Terrible Sprite controls, need help.
#1
I have this sprite here. The controls are dreadful, and my tinkering is fruitless. I copied it from an Asteroids game, and took out the friction, but I broke it.

Sometimes the "thrust" will cause it to slow/break.

How fast you go is effected strongly by your angle.


Here is the sprite and the event handling. I haven't success yet with this type of controls yet. Any advice would be appreciated.

ship_max_speed = 2
ship_max_rtspd = 3


class Ship(pygame.sprite.Sprite):
    def __init__(self, x, y):
        
        pygame.sprite.Sprite.__init__(self)
        self.ship_img = pygame.Surface((15,10), pygame.SRCALPHA)
        self.ship_img.fill(red)
        self.image = self.ship_img
        self.rect = self.image.get_rect()
        
        self.hit_rect = ship_HIT_RECT
        self.hit_rect.center = self.rect.center
        self.rect.centerx = x
        self.rect.centery = y
       
        self.hspeed = 0
        self.vspeed = 0
        self.dir = 0
        self.rtspd = 0
        self.thrust = False   
        
        
    def update(self):
        
        speed = math.sqrt(self.hspeed**2 + self.vspeed**2)
        if self.thrust:
            
            if speed < ship_max_speed:
                self.hspeed += math.cos(self.dir * math.pi / 180)
                self.vspeed += math.sin(self.dir * math.pi / 180)
            else:
                self.hspeed = ship_max_speed * math.cos(self.dir * math.pi / 180)
                self.vspeed = ship_max_speed * math.sin(self.dir * math.pi / 180)
        
        self.rect.centerx += self.hspeed
        self.rect.centery += self.vspeed
        self.dir = (self.dir + self.rtspd) % 360
        self.image = pygame.transform.rotate(self.ship_img, -self.dir)
        self.rect = self.image.get_rect(center=self.rect.center)

####################################################################################

def get_event(self, event):
        
        self.ship.rtspd = 0
        
        if event.type == pygame.QUIT:
            gameState = "Exit"
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                self.ship.thrust = True
                
            if event.key == pygame.K_LEFT:
                self.ship.rtspd = -ship_max_rtspd
            if event.key == pygame.K_RIGHT:
                self.ship.rtspd = ship_max_rtspd            
            
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_UP:
                self.ship.thrust = False
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                self.ship.rtspd = 0
    
Reply
#2
Example.
import os
import math
import pygame
from pygame.sprite import Sprite

class Scene:
    def on_draw(self, surface): pass
    def on_event(self, event): pass
    def on_update(self, delta): pass

class Manager:
    def __init__(self, caption, width, height, center=True):
        if center:
            os.environ['SDL_VIDEO_CENTERED'] = '1'

        # Basic pygame setup
        pygame.display.set_caption(caption)
        self.surface = pygame.display.set_mode((width, height))
        self.rect = self.surface.get_rect()
        self.clock = pygame.time.Clock()
        self.running = False
        self.delta = 0
        self.fps = 60

        # Scene Interface
        self.scene = Scene()

    def mainloop(self):
        self.running = True
        while self.running:
            for event in pygame.event.get():
                self.scene.on_event(event)

            self.scene.on_update(self.delta)
            self.scene.on_draw(self.surface)
            pygame.display.flip()
            self.delta = self.clock.tick(self.fps)

    def quit(self):
        self.running = False

class Ship(Sprite):
    def __init__(self, image, position, anchor="center"):
        self.image = image
        self.original_image = image
        self.rect = image.get_rect(**{anchor: position})
        self.position = pygame.Vector2(self.rect.center)

        self.angle = 0
        self.speed = 0
        self.max_speed = 0.1
        self.thrust = 0.00005
        self.rotate_speed = 0.08
        self.friction = 0.99
        self.drift = 0.01
        self.calculate_direction()

    def calculate_direction(self):
        rads = math.radians(self.angle)
        self.direction = pygame.Vector2(math.sin(rads), math.cos(rads))

    def draw(self, surface):
        surface.blit(self.image, self.rect)

    def movement(self, keys, delta):
        if keys[pygame.K_UP]:
            self.speed += self.thrust * delta
            if self.speed > self.max_speed:
                self.speed = self.max_speed
        elif keys[pygame.K_DOWN]:
            self.speed -= self.thrust * delta / 2
            if self.speed < -self.max_speed / 2:
                self.speed = -self.max_speed / 2
        else:
            self.speed = self.speed * self.friction
            if -self.drift < self.speed < self.drift:
                self.speed = 0

        if keys[pygame.K_LEFT]:
            self.angle = (self.angle + self.rotate_speed * delta) % 360
            self.update_rotation()
        elif keys[pygame.K_RIGHT]:
            self.angle = (self.angle - self.rotate_speed * delta) % 360
            self.update_rotation()

        self.position += self.direction * self.speed * delta
        self.rect.center = self.position

    def update_rotation(self):
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.calculate_direction()

class ShipScene(Scene):
    def __init__(self, manager):
        self.manager = manager
        self.create_ship()

    def create_ship(self):
        image = pygame.Surface((15, 10), pygame.SRCALPHA)
        image.fill(pygame.Color('red'))

        self.ship = Ship(image, self.manager.rect.center)

    def on_draw(self, surface):
        surface.fill(pygame.Color('black'))
        self.ship.draw(surface)

    def on_event(self, event):
        if event.type == pygame.QUIT:
            self.manager.quit()

    def on_update(self, delta):
        keys = pygame.key.get_pressed()
        self.ship.movement(keys, delta)

def main():
    pygame.init()
    manager = Manager("Ship Thrust Example", 800, 600)
    manager.scene = ShipScene(manager)
    manager.mainloop()

if __name__ == "__main__":
    main()
99 percent of computer problems exists between chair and keyboard.
Reply
#3
In your example the sprite drives like a car. I'm looking for it to drift in a straight line unless the thruster is engaged. Much like the original "Asteroids" game.
Reply
#4
So you want it more like this.
class Ship(Sprite):
    def __init__(self, image, position, anchor="center"):
        self.image = image
        self.original_image = image
        self.rect = image.get_rect(**{anchor: position})
        self.position = pygame.Vector2(self.rect.center)
        self.start_position = pygame.Vector2(position)

        self.angle_change = False
        self.angle = 180
        self.speed = 0
        self.max_speed = 0.1
        self.thrust = 0.00005
        self.rotate_speed = 0.06
        self.friction = 0.999
        self.drift = 0.01
        self.direction = self.calculate_direction()

    def calculate_direction(self):
        rads = math.radians(self.angle)
        return pygame.Vector2(math.sin(rads), math.cos(rads))

    def draw(self, surface):
        surface.blit(self.image, self.rect)

    def movement(self, keys, delta):
        if keys[pygame.K_r]:
            self.reset()

        if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
            if self.angle_change:
                self.angle_change = False
                self.direction = self.calculate_direction()

            self.speed += self.thrust * delta
            if self.speed > self.max_speed:
                self.speed = self.max_speed
        else:
            self.speed = self.speed * self.friction
            if -self.drift < self.speed < self.drift:
                self.speed = 0

        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.angle = (self.angle + self.rotate_speed * delta) % 360
            self.update_rotation()
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.angle = (self.angle - self.rotate_speed * delta) % 360
            self.update_rotation()

        self.position += self.direction * self.speed * delta
        self.rect.center = self.position

    def reset(self):
        self.angle = 180
        self.direction = self.calculate_direction()
        self.update_rotation()
        self.rect.center = self.start_position
        self.position = pygame.Vector2(self.start_position)
        self.angle_change = False
        self.speed = 0

    def update_rotation(self):
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.angle_change = True
99 percent of computer problems exists between chair and keyboard.
Reply
#5
I get this error. I suspect that I'm not giving the right argument to "ship = Ship()"
Error:
Warning (from warnings module): File "C:/Users/owner/Desktop/Python3/Projects/Asteroids/Asteroids controles.py", line 64 self.rect.center = self.position DeprecationWarning: an integer is required (got type float). Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.
import pygame
import math
pygame.init()

window = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()


class Ship(pygame.sprite.Sprite):
    def __init__(self, position, anchor="center"):
        
        self.original_image = pygame.Surface((10, 20))
        self.original_image.fill((100, 200, 200))
        self.image = self.original_image
        
        self.rect = self.image.get_rect(**{anchor: position})
        
        self.position = pygame.Vector2(self.rect.center)
        
        self.start_position = pygame.Vector2(position)
 
        self.angle_change = False
        self.angle = 180
        self.speed = 0
        self.max_speed = 0.1
        self.thrust = 0.00005
        self.rotate_speed = 0.06
        self.friction = 0.999
        self.drift = 0.01
        self.direction = self.calculate_direction()
 
    def calculate_direction(self):
        rads = math.radians(self.angle)
        return pygame.Vector2(math.sin(rads), math.cos(rads))
 
    def draw(self, surface):
        surface.blit(self.image, self.rect)
 
    def movement(self, keys, delta):
        if keys[pygame.K_r]:
            self.reset()
 
        if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
            if self.angle_change:
                self.angle_change = False
                self.direction = self.calculate_direction()
 
            self.speed += self.thrust * delta
            if self.speed > self.max_speed:
                self.speed = self.max_speed
        else:
            self.speed = self.speed * self.friction
            if -self.drift < self.speed < self.drift:
                self.speed = 0
 
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.angle = (self.angle + self.rotate_speed * delta) % 360
            self.update_rotation()
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.angle = (self.angle - self.rotate_speed * delta) % 360
            self.update_rotation()
 
        self.position += self.direction * self.speed * delta
        
        self.rect.center = self.position
        
    def reset(self):
        self.angle = 180
        self.direction = self.calculate_direction()
        self.update_rotation()
        self.rect.center = self.start_position
        self.position = pygame.Vector2(self.start_position)
        self.angle_change = False
        self.speed = 0
 
    def update_rotation(self):
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.angle_change = True
    def update(self, keys, delta):
            self.movement(keys, delta)
            self.draw(window)

        
ship = Ship((400, 400))
run = True
while run:
    delta = clock.tick(60) / 1000.0
    keys = pygame.key.get_pressed()
    ship.update(keys, delta)
    
    pygame.display.flip()
   
Reply
#6
I get no error when I run your code. What yours python and pygame version.
My python version 3.7.3, pygame version 1.9.6.

All my math is base on delta not being divide by 1000.
delta = clock.tick(60)
You also going to want to change original surface. To alpha state and copy original image not assign.
    self.original_image = pygame.Surface((10, 20), pygame.SRCALPHA)
    self.original_image.fill((100, 200, 200))
    self.image = self.original_image.copy()

Could try: On line 65.
self.rect.center = tuple(map(int, self.position))
99 percent of computer problems exists between chair and keyboard.
Reply
#7
I did all your changes and it runs, but nothing happens and game window freezes. It keeps running even after the window freezes, but I don't think it's taking input.

import pygame
import math
pygame.init()

window = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()
black = (0, 0, 0)

class Ship(pygame.sprite.Sprite):
    def __init__(self, position, anchor="center"):
        
        self.original_image = pygame.Surface((10, 20), pygame.SRCALPHA)
        self.original_image.fill((100, 200, 200))
        self.original_image.set_colorkey(black)
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect(**{anchor: position})
        self.position = pygame.Vector2(self.rect.center)
        self.start_position = pygame.Vector2(position)
        self.angle_change = False
        self.angle = 180
        self.speed = 0
        self.max_speed = 0.1
        self.thrust = 0.00005
        self.rotate_speed = 0.06
        self.friction = 0.999
        self.drift = 0.01
        self.direction = self.calculate_direction()
 
    def calculate_direction(self):
        rads = math.radians(self.angle)
        return pygame.Vector2(math.sin(rads), math.cos(rads))
 
    def draw(self, surface):
        surface.blit(self.image, self.rect)
 
    def movement(self, keys, delta):
        
        if keys[pygame.K_r]:
            self.reset()
 
        if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
            if self.angle_change:
                self.angle_change = False
                self.direction = self.calculate_direction()
 
            self.speed += self.thrust * delta
            if self.speed > self.max_speed:
                self.speed = self.max_speed
        else:
            self.speed = self.speed * self.friction
            if -self.drift < self.speed < self.drift:
                self.speed = 0
 
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.angle = (self.angle + self.rotate_speed * delta) % 360
            self.update_rotation()
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.angle = (self.angle - self.rotate_speed * delta) % 360
            self.update_rotation()
 
        self.position += self.direction * self.speed * delta
        
        self.rect.center = tuple(map(int, self.position))
        
        
    def reset(self):
        self.angle = 180
        self.direction = self.calculate_direction()
        self.update_rotation()
        self.rect.center = self.start_position
        self.position = pygame.Vector2(self.start_position)
        self.angle_change = False
        self.speed = 0
 
    def update_rotation(self):
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.angle_change = True
        
    def update(self, keys, delta):
            self.movement(keys, delta)
            self.draw(window)

        
ship = Ship((400, 400))
run = True
while run:
    delta = clock.tick(60)
    keys = pygame.key.get_pressed()
    ship.update(keys, delta)
    pygame.display.flip()
Reply
#8
I added event loop when I tested it.
import pygame
import math
pygame.init()

window = pygame.display.set_mode((800, 800))
clock = pygame.time.Clock()


class Ship(pygame.sprite.Sprite):
    def __init__(self, position, anchor="center"):

        self.original_image = pygame.Surface((10, 20), pygame.SRCALPHA)
        self.original_image.fill((100, 200, 200))
        self.image = self.original_image.copy()

        self.rect = self.image.get_rect(**{anchor: position})

        self.position = pygame.Vector2(self.rect.center)

        self.start_position = pygame.Vector2(position)

        self.angle_change = False
        self.angle = 180
        self.speed = 0
        self.max_speed = 0.1
        self.thrust = 0.00005
        self.rotate_speed = 0.06
        self.friction = 0.999
        self.drift = 0.01
        self.direction = self.calculate_direction()

    def calculate_direction(self):
        rads = math.radians(self.angle)
        return pygame.Vector2(math.sin(rads), math.cos(rads))

    def draw(self, surface):
        surface.blit(self.image, self.rect)

    def movement(self, keys, delta):
        if keys[pygame.K_r]:
            self.reset()

        if keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]:
            if self.angle_change:
                self.angle_change = False
                self.direction = self.calculate_direction()

            self.speed += self.thrust * delta
            if self.speed > self.max_speed:
                self.speed = self.max_speed
        else:
            self.speed = self.speed * self.friction
            if -self.drift < self.speed < self.drift:
                self.speed = 0

        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.angle = (self.angle + self.rotate_speed * delta) % 360
            self.update_rotation()
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.angle = (self.angle - self.rotate_speed * delta) % 360
            self.update_rotation()

        self.position += self.direction * self.speed * delta

        self.rect.center = self.position

    def reset(self):
        self.angle = 180
        self.direction = self.calculate_direction()
        self.update_rotation()
        self.rect.center = self.start_position
        self.position = pygame.Vector2(self.start_position)
        self.angle_change = False
        self.speed = 0

    def update_rotation(self):
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.angle_change = True

    def update(self, keys, delta):
            self.movement(keys, delta)
            self.draw(window)


ship = Ship((400, 400))
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    delta = clock.tick(60)
    keys = pygame.key.get_pressed()
    window.fill(pygame.Color('black'))
    ship.update(keys, delta)

    pygame.display.flip()
99 percent of computer problems exists between chair and keyboard.
Reply
#9
It runs well, but something is wonky with the speed. If you thrust and then you rotate 180 deg and thrust again your speed stays the same but your direction is reversed. I think it applies your current speed to the direction you are facing, even if it's directly opposite to your previous thrust. There is no way to do a "reverse burn" in order to slow or stop.
Reply
#10
You should always handle events. If you ignore it. The queue will fill up.
You also need to call events.get, events.poll, or event.pump to update pygame.key.get_pressed.
99 percent of computer problems exists between chair and keyboard.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] Sprite image.get_rect() moves sprite to 0, 0 michael1789 2 4,600 Dec-13-2019, 08:37 PM
Last Post: michael1789
  Sprite not rendering Clunk_Head 2 2,140 Oct-03-2019, 11:27 AM
Last Post: Clunk_Head
  Need help making a sprite GalaxyCoyote 4 3,238 Aug-11-2019, 09:12 PM
Last Post: metulburr
  moving a sprite pfaber11 3 2,587 May-15-2019, 12:52 PM
Last Post: pfaber11
  [PyGame] Need Help With Sprite ghost0fkarma 2 3,283 Jan-09-2018, 02:14 PM
Last Post: ghost0fkarma
  [PyGame] Snake controls not working jakegold98 5 6,379 Dec-12-2017, 01:45 AM
Last Post: Windspar

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020