A downloadable game for Windows

Dragster

Project Description

A recreation of classic Atari dragster game with better graphics.  

Assignment Rubrics

  • 1 point - have a title screen on which pressed a given key to starts the countdown timer
  • 3 points - have a countdown timer of some sort (during which pressing the accelerator will result in a false start)
  • 1 point - pressing a given key will start moving the player forward
  • 1 point - the player accelerates the longer they hold down the key
  • 1 point - a results screen appears after crossing the finish line or false starting
  • 2 points - the total travel time (time from the end of the countdown to crossing the finish line)/if the player false started is shown on the results screen
  • 1 point - a given key allows the game to restart when on the results screen
  • Extra Credit (2 points) - implement a system of gear shifting or another method of changing the player's speed
  • Extra Credit (1 point) - add particles

My Code

# Dragster Game, Phew
AIR_COEFF = 3
GEAR_SHIFT_CD = 30
gearFrameAfterFirstPress = 0
renderQueue = []
particles = []
MAIN_MENU = 0
COUNT_DOWN = 1
PLAYING = 2
RESULT = 3
gameState = MAIN_MENU
countdownTime = 3  # second
gameTimer = 0
startTime = 0
countdownStarted = False
gameOver = False
GOMsg = "Err"
def setup():
    global car, renderQueue, fg, particles
    size(800,600)
    loadAssets()
    car = Car(7000, 4, 90, 500000)
    bg = CityBackground(T_BG1, T_BG2, 000, car, 100.0)
    mg = CityBackground(T_MG1, T_MG2, 000, car, 60.0)
    fg = CityBackground(T_FG1, T_FG2, 000, car, 30.0)
    renderQueue.append(bg)
    renderQueue.append(mg)
    renderQueue.append(fg)
    renderQueue.append(car)
    #Particle Init
    for i in range(42):
        particles.append(Particle(random(800), random(600), car.spd / -100, 200))
    print("init success")
def draw():
    global renderQueue, car, gameState, countdownTime, startTime, gameTimer, gameOver, GOMsg, countdownStarted
    
    if gameState == MAIN_MENU:
        mainMenu()
    
    elif gameState == COUNT_DOWN:
        #Init all data
        if countdownStarted:
            carInit()
            gameTimer = millis()
            #print(millis())
            countdownStarted = False
            gameOver = False
            startTime = gameTimer + 1000 * countdownTime
        else:
            for obj in renderQueue:
                obj.update()
                obj.render()
            
            if car.gear > 0:
                gameOver = True
                #print(car.gear)
                gameState = RESULT
                GOMsg = "Early Fault Start!"
                
            if millis() > gameTimer + countdownTime * 1000:
                gameState = PLAYING
                
            ShowCountdown()
            ControllerUpdate()
            displayUI()
        
    elif gameState == PLAYING:
        background(0)
        ControllerUpdate()
        
        for obj in renderQueue:
            obj.update()
            obj.render()
            
        for p in particles:
            p.update()
            p.render()
            if p.lifespan <= 0:
                particles.remove(p)
                particles.append(Particle(random(width + 400), random(height), car.spd / -100, 200))
        
        displayUI()
        #displayDebugInfo()
        checkGameOver()
        
        #print("displayed Debug info")
        
    elif gameState == RESULT:
        resultPage()
def ControllerUpdate():
    global gearFrameAfterFirstPress
    if keyPressed:
        # Shift Gear Up
        if key == ' ':
            if gearFrameAfterFirstPress == 0:
                car.gearSwitch = True
                gearFrameAfterFirstPress += 1
            else:
                car.gearSwitch = False
                gearFrameAfterFirstPress += 1
        # Full Throttle
        if key == 'a': 
            car.throttle = True
    else:
        if gearFrameAfterFirstPress < GEAR_SHIFT_CD and gearFrameAfterFirstPress != 0:
            gearFrameAfterFirstPress += 1
            car.gearSwitch = False
        elif gearFrameAfterFirstPress >= GEAR_SHIFT_CD:
            gearFrameAfterFirstPress = 0
            car.gearSwitch = False
            print("CD complete")
        car.gearSwitch = False
        car.throttle = False
        
def displayDebugInfo():
    global car, fg
    textSize(20)
    fill(0)
    textAlign(LEFT)
    text("Speed: " + str(car.spd), 0, 30)
    text("Acce:  " + str(car.acce), 0, 60)
    text("airRes:" + str(car.airRes), 0, 90)
    text("Gear:  " + str(car.gear), 0, 120)
    text("Dist:  " + str(car.curDist) + "/" + str(car.goalDist), 0, 150)
    text("Rev:   " + str(car.rev) + "/" + str(car.maxRev), 0, 180)
    text("fgPosX:" + str(fg.imgAX) + "/" + str(fg.imgBX) + str(fg.imgAX - fg.imgBX), 0, 210)
    text("GState:" + str(gameState), 0, 240)
                             
def loadAssets():
    global T_BG1, T_BG2, T_MG1, T_MG2, T_FG1, T_FG2, T_Car, T_Tire
    T_BG1 = loadImage("BackGround.png")
    T_BG2 = loadImage("BackGround.png")
    T_MG1 = loadImage("MiddleGround.png")
    T_MG2 = loadImage("MiddleGround.png")
    T_FG1 = loadImage("ForeGround.png")
    T_FG2 = loadImage("ForeGround.png")
    T_Car = loadImage("car.png")
    T_Tire = loadImage("tire.png")
    
def mainMenu():
    background(0)  
    fill(255) 
    textSize(32)
    textAlign(CENTER)
    text("Press P to Play", width / 2, height / 2)
    textAlign(LEFT)
    textSize(20)
    text("Press SpaceBar to shift gear up", width / 2 - 300, height / 2 + 100)
    text("Press A to throttle Up", width / 2 - 300, height / 2 + 130)
def resultPage():
    background(50)  
    fill(255)  
    textSize(32)
    textAlign(CENTER)
    text("Press R to Reset", width / 2, height / 2)
    textSize(50)
    text(GOMsg, width / 2, height / 2 - 100)
    
def ShowCountdown():
    remainingTime = countdownTime - int((millis() - gameTimer) / 1000)
    #print(startTime)
    fill(0, 0, 0, 120)
    rectMode(CORNER)
    rect(0, 0, width, height)
    fill(255)
    textSize(200)
    textAlign(CENTER)
    text(str(remainingTime), width / 2, height / 2)
def carInit():
    global car
    car.selfInit()
def checkGameOver():
    global gameOver, GOMsg, gameState
    if car.rev > car.maxRev:
        GOMsg = "You lose! Engine Exploded"
        gameOver = True
        
    elif car.curDist > car.goalDist:
        tempSec = int(millis() - startTime) / 1000
        tempMils = nf(int(millis() - startTime) % 1000, 3)
        GOMsg = ("You win! Time: " + str(tempSec) + "." + str(tempMils) + "s")
        gameOver = True
    
    if gameOver:
        gameState = 3
def displayUI():
    global car
    # Display Rev
    revRatio = float(car.rev) / float(car.maxRev)
    fill(0, 255, 0) 
    if revRatio > 0.8:
        fill(255, 0, 0)
    rect(20, height - 50, revRatio * 200, 30)
    #print(revRatio)
    # Display Gear
    fill(255)
    textSize(20)
    text("Gear: " + str(car.gear), 250, height - 30)
    # Display Current Time
    if gameState == COUNT_DOWN:
        text("Time: 0.000", 400, height - 30)
    else: 
        tempSec = int(millis() - startTime) / 1000
        tempMils = nf(int(millis() - startTime) % 1000, 3)
        text("Time: " + str(tempSec) + "." + str(tempMils), 400, height - 30)
def keyPressed():
    global gameState, countdownStarted
    if key == 'p' or key == 'P':
        if gameState == MAIN_MENU:
            gameState = COUNT_DOWN
            countdownStarted = True
            
    elif key == 'r' or key == 'R':
        #if gameState == RESULT:
        gameState = MAIN_MENU
    
class Car(object):
    def __init__(self, tempMaxRev, tempMaxGear, tempFrameToFullRev, tempGoalDistance):
        self.spd = 0.0
        self.acce = 0.0
        self.airRes = 0.0
        self.rev = 1000
        self.frameToFullRev = tempFrameToFullRev
        self.maxRev = tempMaxRev
        self.throttle = False
        self.blown = False
        self.gearSwitch = False
        self.gear = 0
        self.maxGear = tempMaxGear
        self.curDist = 0
        self.goalDist = tempGoalDistance
        
    def selfInit(self):
        self.spd = 0.0
        self.acce = 0.0
        self.airRes = 0.0
        self.rev = 1000
        self.throttle = False
        self.blown = False
        self.gearSwitch = False
        self.gear = 0
        self.curDist = 0
        
    def update(self):
        # Check if car is blown first
        self.checkBlown()
        if self.blown:
            # if Blown, slowly decrease it's spd till 0
            if self.spd < 1:
                self.spd = 0
            else:
                self.spd -= self.spd / 7
        else:
            self.GearShifting()
            self.Calculating()
            
    
    # Shifting the car's gear
    def GearShifting(self):
        if self.gearSwitch == True:
            if self.gear < self.maxGear:
                self.gear += 1
            else:
                self.rev += self.maxRev / 5
        
    # Calculate Current Acce, spd, distance
    def Calculating(self):
        if self.throttle:
            self.rev += (self.maxRev / self.frameToFullRev) / (self.gear + 1)
            # print("Throttle on")
        else:
            if self.rev > 1000:
                self.rev -= (self.maxRev / self.frameToFullRev) / (self.gear + 1) * 2
        mappedRev = map(self.rev, 0, self.maxRev, 0, 1)
        if self.gear == 0:
            engineForce = 0
        else:
            engineForce = mappedRev * (5 - self.gear)
            # print(self.rev, self.maxRev, self.frameToFullRev, self.gear)
        # TODO?: Make car move more realistically
        # mappedRev = map(self.rev, 0, maxRev, 0, 1)
        # engineForce = -0.63 * sq(mappedRev) + 0.47 * mappedRev + 0.76
        self.airRes = AIR_COEFF * sq(self.spd)
        # self.acce = engineForce - self.airRes
        # engineForce = self.rev * self.gear
        self.acce = engineForce
        self.spd += self.acce
        self.curDist += self.spd
    
    def render(self):
        self.renderCar()
        #self.renderBackground()
        
    def renderCar(self):
        global T_Car, T_Tire
        imageMode(CENTER)
        tempX = map(self.curDist, 0, self.goalDist, 150, width + 150)
        tempY = 500
        image(T_Car, tempX, tempY)
        tireX = map(self.curDist, 0, self.goalDist, 45, width + 45)
        tireY = 506
        pushMatrix()
        translate(tireX, tireY)
        if self.spd != 0:
            rotate(random(0,PI))
        image(T_Tire, 0, 0)
        popMatrix()
        
    # This function is useless now
    # def renderBackground(self):
    #     global T_BG1, T_BG2, T_MG1, T_MG2, T_FG1, T_FG2
    #     imageMode(CENTER)
    #     bgPosX = map(self.goalDist - self.curDist, 0, self.goalDist, 0, 1792 * 2) % (1792 * 2) + (1792 / 2)
    #     bgPosY = 200
    #     image(T_BG1, bgPosX - 400, bgPosY)
    #     image(T_BG2, bgPosX + 1792 - 400, bgPosY)
    
    def checkBlown(self):
        if self.rev > self.maxRev:
            self.blown = True
    
class CityBackground(object):
    def __init__(self, tempImgA, tempImgB, tempY, tempCar, tempSpdCoeff):
        self.imgA = tempImgA
        self.imgB = tempImgB
        self.spd = 0
        self.imgHeight = tempY
        self.imgAX = 0
        self.imgBX = 1792
        self.car = tempCar
        self.spdCoe = tempSpdCoeff
        
    def update(self):
        self.spd = int(self.car.spd / self.spdCoe)
        if self.imgAX < -1 * (1792):
            self.imgAX += 1792 * 2
        self.imgAX -= self.spd
        if self.imgBX < -1 * (1792):
            self.imgBX += 1792 * 2
        self.imgBX -= self.spd
        
    def render(self):
        imageMode(CORNER)
        image(self.imgA, self.imgAX, self.imgHeight)
        image(self.imgB, self.imgBX, self.imgHeight)
        
class Particle:
    def __init__(self, tempX, tempY, tempSpd, tempLife):
        #print(30)
        self.initialX = tempX
        self.initialY = tempY
        self.position = PVector(tempX, tempY)
        self.velocity = PVector(random(tempSpd, tempSpd / 3), 0)
        self.amplitude = random(5, 20)
        self.angle = 0
        self.angleVelocity = random(tempSpd/100, tempSpd/30)
        self.trail = []
        self.lifespan = int(random(tempLife - tempLife/10, tempLife + tempLife+10))
        #print(25)
    def update(self):
        self.position.x += self.velocity.x
        self.position.y = self.initialY + self.amplitude * sin(self.angle)
        self.angle += self.angleVelocity
        self.trail.append(self.position.copy())
        if len(self.trail) > 20:
            self.trail.pop(0)
        self.lifespan -= 1
    def render(self):
        if self.lifespan > 0:
            noFill()
            opacity = map(self.lifespan, 0, 200, 0, 255)
            stroke(255, opacity)
            for i in range(1, len(self.trail)):
                p1 = self.trail[i - 1]
                p2 = self.trail[i]
                segmentRatio = float(i) / len(self.trail)
                if segmentRatio < 0.5:
                    width = map(segmentRatio, 0, 0.5, 0.5, 3)
                else:
                    width = map(segmentRatio, 0.5, 1, 3, 0.5)
                angle = atan2(p2.y - p1.y, p2.x - p1.x) + HALF_PI
                xOff = width * cos(angle)
                yOff = width * sin(angle)
                beginShape()
                vertex(p1.x + xOff, p1.y + yOff)
                vertex(p1.x - xOff, p1.y - yOff)
                vertex(p2.x - xOff, p2.y - yOff)
                vertex(p2.x + xOff, p2.y + yOff)
                endShape(CLOSE)

Screenshot


Simple Painter App Gameplay

Download

Download
application.windows64.zip 44 MB

Install instructions

Java 8 required

Leave a comment

Log in with itch.io to leave a comment.