Reputation: 516
I'm making a game with Asteroids like controls, UP arrow for acceleration, RIGHT arrow to rotate clockwise and LEFT arrow counterclockwise so for example if I want to slow down I need to rotate ship 180° and accelerate. I'm able to rotate and also for this purpose I drew two vectors (velocity and heading). I'm trying to achieve this type of movement with vectors but maybe I shouldn't ? So here's what I came up with.
import pygame
import sys
from pygame.locals import * # pygame.locals.QUIT --> QUIT
# Vector2
vec = pygame.math.Vector2
# initialize pygame
pygame.init()
FPS = 60 # frames per second
fps_clock = pygame.time.Clock()
# set up the window
WIDTH = 800
HEIGHT = 800
DISPLAY = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('VECTOR_2')
FONT = pygame.font.Font(None, 24)
# RGB colors
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
MAX_SPEED = 9
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
# set ship image
self.image = pygame.Surface((70, 50), pygame.SRCALPHA)
pygame.draw.polygon(self.image, (50, 120, 180), ((35, 0), (0, 35), (70, 35)))
self.original_image = self.image
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.position = vec(WIDTH / 2, HEIGHT / 2)
self.vel = vec(0, 0)
self.acceleration = vec(0, 0)
# heading vector
self.heading = vec(0, -1) # upwards
self.rect.center = self.position
self.angle_speed = 0
self.angle = 0
def update(self):
self.acceleration = vec(0, 0)
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.acceleration.x = -0.2
if keys[K_RIGHT]:
self.acceleration.x = 0.2
if keys[K_UP]:
self.acceleration.y = -0.2
if keys[K_DOWN]:
self.acceleration.y = 0.2
if keys[K_a]:
self.angle_speed = -1
player.rotate()
if keys[K_d]:
self.angle_speed = 1
player.rotate()
# max speed
if self.vel.length() > MAX_SPEED:
self.vel.scale_to_length(MAX_SPEED)
self.vel += self.acceleration
self.position += self.vel
self.rect.center = self.position
def rotate(self):
# rotate the heading vector
self.heading.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
if self.angle > 360:
self.angle -= 360
elif self.angle < 0:
self.angle += 360
self.image = pygame.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def draw_vectors(self, screen):
scale = 20
# vel
pygame.draw.line(screen, GREEN, self.position, (self.position + self.vel * scale), 5)
# desired
pygame.draw.line(screen, RED, self.position, (self.position + self.heading * scale), 5)
def wrap_around_screen(self):
"""Wrap around screen."""
if self.position.x > WIDTH:
self.position.x = 0
if self.position.x < 0:
self.position.x = WIDTH
if self.position.y <= 0:
self.position.y = HEIGHT
if self.position.y > HEIGHT:
self.position.y = 0
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
while True: # game loop
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAY.fill(BLACK)
player.wrap_around_screen()
all_sprites.update()
all_sprites.draw(DISPLAY)
player.draw_vectors(DISPLAY)
txt = FONT.render('angle {:.1f}'.format(player.angle), True, (150, 150, 170))
DISPLAY.blit(txt, (10, 10))
pygame.display.update()
fps_clock.tick(FPS)
Any help would be appreciated
Upvotes: 4
Views: 8404
Reputation: 20438
You can give the self.acceleration
vector a initial length (0.2 in this case), rotate it if the player presses ← or →, and only accelerate (add it to the self.vel
vector) if ↑ or ↓ are pressed.
import sys
import pygame
from pygame.locals import *
vec = pygame.math.Vector2
pygame.init()
FPS = 60
fps_clock = pygame.time.Clock()
WIDTH = 800
HEIGHT = 800
DISPLAY = pygame.display.set_mode((WIDTH, HEIGHT))
BLACK = (0, 0, 0)
MAX_SPEED = 9
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((70, 50), pygame.SRCALPHA)
pygame.draw.polygon(self.image, (50, 120, 180), ((35, 0), (0, 35), (70, 35)))
self.original_image = self.image
self.position = vec(WIDTH / 2, HEIGHT / 2)
self.rect = self.image.get_rect(center=self.position)
self.vel = vec(0, 0)
self.acceleration = vec(0, -0.2) # The acceleration vec points upwards.
self.angle_speed = 0
self.angle = 0
def update(self):
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
self.angle_speed = -1
player.rotate()
if keys[K_RIGHT]:
self.angle_speed = 1
player.rotate()
# If up or down is pressed, accelerate the ship by
# adding the acceleration to the velocity vector.
if keys[K_UP]:
self.vel += self.acceleration
if keys[K_DOWN]:
self.vel -= self.acceleration
# max speed
if self.vel.length() > MAX_SPEED:
self.vel.scale_to_length(MAX_SPEED)
self.position += self.vel
self.rect.center = self.position
def rotate(self):
# Rotate the acceleration vector.
self.acceleration.rotate_ip(self.angle_speed)
self.angle += self.angle_speed
if self.angle > 360:
self.angle -= 360
elif self.angle < 0:
self.angle += 360
self.image = pygame.transform.rotate(self.original_image, -self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
def wrap_around_screen(self):
"""Wrap around screen."""
if self.position.x > WIDTH:
self.position.x = 0
if self.position.x < 0:
self.position.x = WIDTH
if self.position.y <= 0:
self.position.y = HEIGHT
if self.position.y > HEIGHT:
self.position.y = 0
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
player.wrap_around_screen()
all_sprites.update()
DISPLAY.fill(BLACK)
all_sprites.draw(DISPLAY)
pygame.display.set_caption('angle {:.1f} accel {} accel angle {:.1f}'.format(
player.angle, player.acceleration, player.acceleration.as_polar()[1]))
pygame.display.update()
fps_clock.tick(FPS)
Upvotes: 5