Source code for visualiser.vehicles

'''Vehicle classes used by the game'''
import sys, random
from os import listdir
from os.path import join, dirname

from config import SimulatorConfig, GUIConfig
from .util import SPRITE_DIR, OBSTACLE_SPRITE_PREFIX, loadSprite, Pos, Vector

guiConf = GUIConfig()
conf = SimulatorConfig()
OBSTACLE_SPRITES = listdir(join(SPRITE_DIR, OBSTACLE_SPRITE_PREFIX))

#: :class:`dict` mapping obstacle sprites onto their respective :class:`float`
#: scales
SPRITE_SCALES = {
    'car.png':    .4,
    'lorry.png':  .75,
    'police.png': .42,
    'taxi.png':   .35,
    'truck.png':  .5,
    'van.png':    .55,
    'viper.png':  .37,
}

[docs]class Vehicle: '''A vehicle in the game :param game: The parent :class:`~visualiser.visualiser.SimulatorVisualiser` ''' def __init__(self, game): # real 2D velocity vector, applied each tick self.velocity = Vector() #: A reference to the vehicle's :class:`~pygame.Rect` self.rect = None self._game = game self._lane = 0 #: (:class:`float`) Size multiplier of the sprite self._spriteScale = 1 #: :class:`str` name of the sprite file, relative to the #: sprites/obstacles directory self._sprite = 'obstacle.png' self._image = None self._pos = Pos(self) # real 2D position Vector self._speed = 0 # virtual speed (m/s) @property def pos(self): '''The position vector of the vehicle :getter: Get position :setter: Set the position :type: :class:`~visualiser.util.Pos`''' return self._pos @pos.setter def pos(self, vec): self.rect.x = vec.x self.rect.y = vec.y self._pos = vec @property def speed(self): '''The virtual forward speed of the vehicle, in metres per second :getter: Get the vehicle speed :setter: Set the vehicle speed in m/s ''' return self._speed @speed.setter def speed(self, val): self._speed = val @property def sprite(self): '''The sprite object. Uses :py:attr:`~_sprite` as the file name of the image to load :getter: Return the sprite :type: :class:`pygame.Surface` ''' if not self._image is None: return self._image self._image = loadSprite(self._sprite, self._spriteScale) return self._image @property def lane(self): '''The current lane of the vehicle :getter: Get the current lane :setter: Set the lane of the vehicle. The vehicle is centered in its lane. Will only accept lanes available :type: :class:`int` ''' return self._lane @lane.setter def lane(self, val): off = conf.OffroadAllowed if val not in range(0 if off else 1, 5 if off else 4): return self._lane = int(val) # start with just offroad/2, subtract width/2 self.pos.x = -self.rect.size[0] /2 if val < 1: self.pos.x += conf.OffroadWidth /2 return # add offroad width, width of each lane to left, plus half self.pos.x += conf.OffroadWidth +max(0, val -1) *conf.LaneWidth \ +(conf.LaneWidth if val < 4 else conf.OffroadWidth) /2
[docs] def colliding(self, veh): '''Return whether this vehicle is colliding with Vehicle veh''' return self.rect.colliderect(veh.rect)
[docs] def draw(self, canvas): '''Draw the vehicle''' canvas.blit(self.sprite, self.rect)
[docs] def tick(self, dt): '''Called regularly to perform update logic. Updates vehicle position using its velocity''' self.pos += self.velocity *dt
[docs]class Obstacle(Vehicle): '''An obstacle vehicle that the main car has to avoid :param game: The parent :class:`~visualiser.visualiser.SimulatorVisualiser` ''' def __init__(self, game): super().__init__(game) sprite = random.choice(OBSTACLE_SPRITES) self._sprite = OBSTACLE_SPRITE_PREFIX + sprite self._spriteScale = SPRITE_SCALES[sprite] self.rect = self.sprite.get_rect() #: :class:`bool` indicating whether this vehicle has collided with the #: user car self._collided = False #: :class:`bool` indicating whether this vehicle has collided with the #: agent car self._agentCollided = False self.lane = 0 self.speed = conf.ObstacleSpeed self.pos.y = -self.rect.size[1] +1 # start just above screen @property def hasCollided(self): ''' :getter: Return whether the vehicle has just collided this tick. Updates :py:attr:`_collided` ''' collision = self._game.car.rect.colliderect(self.rect) ret = collision and not self._collided self._collided = collision or self._collided return ret @property def hasCollidedAgent(self): ''' :getter: Returns whether the vehicle has started colliding with agent car this tick. Updates :py:attr:`_agentCollided` ''' collision = self._game.agentCar.rect.colliderect(self.rect) ret = collision and not self._agentCollided self._agentCollided = collision or self._agentCollided return ret @property def speed(self): '''The virtual forward speed of the vehicle, in metres per second :getter: Get the vehicle speed :setter: Set the vehicle speed in m/s. Also sets the real velocity, which is relative to the user car''' return self._speed @speed.setter def speed(self, val): self._speed = val # set velocity relative to driver self.velocity.y = -(val -conf.CarSpeed) \ *conf.PixelMetreRatio
[docs] def tick(self, dt): '''Called regularly to perform update logic. Return False to indicate vehicle is entirely off screen and should be removed''' super().tick(dt) return self._game.canvas.get_rect().colliderect(self.rect)
[docs]class Car(Vehicle): '''The controlled car :param game: The parent :class:`~visualiser.visualiser.SimulatorVisualiser` ''' def __init__(self, game): super().__init__(game) self._spriteScale = conf.CarScale self._sprite = 'car.png' self.rect = self.sprite.get_rect() self.pos.y = guiConf.Height -self.rect.size[1] -25 self.speed = conf.CarSpeed self.lane = 2
[docs] def tick(self, dt): '''Car tick method, currently does nothing''' pass