What if you put extra efforts on video game assignments----3
A downloadable game
Ball Catcher V2
Project Description
A simple falling ball catching game with advanced (compare to original V1) , without using OOB. Use keyboard to control the catcher. Further, there are other types of balls either worth more points or give extra lives.
Assignment Rubrics
- 4 points - use arrays and loops to programmatically move the falling objects (that means more than one falling object!)
- 1 point - the catching object must move based on player input (using the same method as last time is fine)
- 4 points - collision detection between the two objects now done with loops and arrays (distance or rectangular, depending on visuals)
- 1 point - the falling objects respawns to a random location when caught or fall off the screen
- Extra Credit (1 point) - display a score and design a more complex scoring system (eg. different point values, multipliers, etc.)
- Extra Credit (2 points) - add hysteresis to the player controlled object and/or the score display (check out the first minute or so from this video to see what that might look like: Super Mario 64 Coin Count (Links to an external site.))
My Code
#Falling Object Game V2(PesudoVer)
import copy
#Create dict for normal ball
normalBallDict = {
"posX":random(20,580),
"posY":-20,
"rad":15,
"speed":2.3,
"ballType":"normal",
"ballColor":"#ffffff",
"score":10,
"refreshInterval":200,
"minimumRefreshInterval":30,
"intervalIncrement":10,
"acceleration":0.03,
"accelerationIncrement":0.001,
}
#Create dict for slow big ball
slowBallDict = {
"posX":random(30,570),
"posY":-30,
"rad":30,
"speed":0.1,
"ballType":"big",
"ballColor":"#ff6524",
"score":100,
"refreshInterval":600,
"minimumRefreshInterval":60,
"intervalIncrement":20,
"acceleration":0.1,
"accelerationIncrement":0.1,
}
#Create dict for fast w/ extra life ball
fastBallDict = {
"posX":random(30,570),
"posY":-20,
"rad":10,
"speed": 10,
"ballType":"fast",
"ballColor":"#84f23f",
"score":50,
"refreshInterval":800,
"minimumRefreshInterval":100,
"intervalIncrement":30,
"acceleration":0.2,
"accelerationIncrement":0.5,
}
#Create Catcher's dict
catcher = {
"posX":0,
"posY":0,
"acce":2,
"maxSpd":15,
"curSpd":0, #define moving to left as "negative" speed, and vise versa
"width":200,
"height":20,
"cornerRounding":10,
"btmDistance":100,
"yTolerance":10,
"color":"#ffffff",
}
#Game Status, 0 = menu, 1 = in-game, 2 = failscreen (showScore)
gameStatus = 0
#Create list to store all ball's info
ballList = []
#Create some global vars for current playing match
gameInProgress = False
#gameInProgress = True #DEBUG
playerLifesConst = 5
playerLifes = playerLifesConst
trueScore = 0
displayedScore = 0
highestScore = 0
frameSinceGame = 0
normalBallRefreshInterval = normalBallDict['refreshInterval']
slowBallRefreshInterval = slowBallDict['refreshInterval']
fastBallRefreshInterval = fastBallDict['refreshInterval']
screenResolutionWidth = 600
screenResolutionHeight = 800
playerPressedKey = "nothingPressed" #have 3 type, nothing/left/right
def interfaceSelector():
if gameStatus == 0:
drawMenu()
elif gameStatus == 1:
drawGame()
elif gameStatus == 2:
drawScore()
def drawMenu():
#TODO: make menu here, change gameStatus to 1 when player click play
drawColorfulBackground()
drawTitle()
drawInteractiveButton("Play")
def drawColorfulBackground():
cirRad = 50
for column in range(screenResolutionWidth / cirRad + 2):
for row in range(screenResolutionHeight / cirRad + 2):
fill(frameCount % 255, (frameCount + column * cirRad) % 255, (frameCount - row * cirRad) % 255)
circle(column * cirRad + frameCount % cirRad, row * cirRad + frameCount % cirRad, cirRad * 3)
def drawTitle():
#f = loadFont("TheBomb-7B9gw.ttf")
#textFont(f) #Nah, font won't work
textSize(100)
fill("#4f099e")
text("Catcher", 150, 300)
def drawInteractiveButton(inputText):
global gameStatus, trueScore, displayedScore
#the box should be 400 x 100, on 300, 500
if (mouseX < 500 and mouseX > 100 and mouseY < 550 and mouseY > 450):
fill("#1483a8")
rect(300, 500, 460, 130, 30, 30, 30, 30)
fill("#3ccafa")
textSize(100)
text(inputText, 210, 520)
if mousePressed:
gameStatus = 1
trueScore = 0
displayedScore = 0
else:
fill("#3361a6")
rect(300, 500, 400, 100, 20, 20, 20, 20)
fill("#6ea2f0")
textSize(80)
text(inputText, 220, 520)
def drawGame():
#TODO: mainGame goes here
global ballList, gameInProgress, frameSinceGame, gameStatus, catcher, playerLifes, normalBallRefreshInterval, slowBallRefreshInterval, fastBallRefreshInterval
#print("Draw Game Running", gameInProgress) #DEBUG
if gameInProgress == False:
#Initializing game here
gameInProgress = True
#ballList.clear() # Clear function in python is not working in Processing's python D:
#have to use some methods to find a workaround
#Reseting Game
emptyList = []
ballList = emptyList
#add first ball to the game here
newBallDict = copy.deepcopy(normalBallDict)
ballList.append(newBallDict)
playerLifes = playerLifesConst
trueScore = 0
displayedScore = 0
#Init catcher's pos
catcher['posY'] = screenResolutionHeight - catcher['btmDistance']
catcher['posX'] = screenResolutionWidth / 2
#ball's refresh interval reset
normalBallRefreshInterval = normalBallDict['refreshInterval']
slowBallRefreshInterval = slowBallDict['refreshInterval']
fastBallRefreshInterval = fastBallDict['refreshInterval']
print(normalBallRefreshInterval)
#CountFrames since this match start
frameSinceGame = 0
else:
frameSinceGame += 1
#Go scoreBoard if failed
if playerLifes <= 0:
gameInProgress = False
gameStatus = 2
#gameStatus = 1 #test before code scoreboard
#background(0) #nah, I'm not gonna use black background anymore
drawColorfulBackground()
fill(40 ,40 ,40 , 200)
rect(screenResolutionWidth / 2, screenResolutionHeight / 2, screenResolutionWidth, screenResolutionHeight)
ballCreator()
#print("ballCreator Runs!")
ballIterator()
#print("ballIterator Runs!")
updateCatcher()
#print("updateCatcher Runs!")
updateUI()
#print("updateUI Runs!")
def ballCreator():
global ballList, normalBallRefreshInterval, slowBallRefreshInterval, fastBallRefreshInterval
#when frame reached normal ball refresh interval
if (frameSinceGame % normalBallRefreshInterval) == 0:
newBallDict = copy.deepcopy(normalBallDict)
newBallDict['posX'] = random(20,580)
ballList.append(newBallDict)
#print("New normal ball added")
normalBallRefreshInterval -= normalBallDict['intervalIncrement']
if normalBallRefreshInterval <= normalBallDict['minimumRefreshInterval']:
normalBallRefreshInterval = normalBallDict['minimumRefreshInterval']
#when frame reached big ball refresh interval
if (frameSinceGame % slowBallRefreshInterval) == 0:
newBallDict = copy.deepcopy(slowBallDict)
newBallDict['posX'] = random(30,570)
ballList.append(newBallDict)
slowBallRefreshInterval -= slowBallDict['intervalIncrement']
print("New slow ball added")
if slowBallRefreshInterval <= slowBallDict['minimumRefreshInterval']:
slowBallRefreshInterval = slowBallDict['minimumRefreshInterval']
#when frame reached fast ball refresh interval
if (frameSinceGame % fastBallRefreshInterval) == 0:
newBallDict = copy.deepcopy(fastBallDict)
newBallDict['posX'] = random(30,570)
ballList.append(newBallDict)
fastBallRefreshInterval -= fastBallDict['intervalIncrement']
print("New fast ball added")
if fastBallRefreshInterval <= fastBallDict['minimumRefreshInterval']:
fastBallRefreshInterval = fastBallDict['minimumRefreshInterval']
def ballIterator():
global ballList, playerLifes, trueScore
#Check collision, if not colliding, update position
for ballElement in ballList:
if checkBtmCollision(ballElement):
playerLifes -= 1
ballList.remove(ballElement)
elif checkCatcherCollision(ballElement):
if ballElement['ballType'] == "fast":
playerLifes += 1
trueScore += ballElement['score']
#print("score added")
ballList.remove(ballElement)
else:
updateBalls(ballElement) #Include drawing part
def checkBtmCollision(ballElement):
#TODO: check is this ball colliding with Bottom or not, return boolean
ballPosY = ballElement['posY']
ballRad = ballElement['rad']
if (ballPosY + ballRad) > screenResolutionHeight:
#print("touch BTM")
return True
return False
def checkCatcherCollision(ballElement):
#TODO: check is this ball colliding with catcher or not, return boolean
ballPosX = ballElement['posX']
ballPosY = ballElement['posY']
ballRad = ballElement['rad']
#calculate Y-axis collision first, should between a range of tolerance
upperBound = (screenResolutionHeight - catcher['btmDistance']) - (catcher['height'] / 2.0)
lowerBound = upperBound + catcher['yTolerance']
ballLowerBound = ballPosY + ballRad
#then calculate X-axis collision
leftBound = (catcher['posX'] - (catcher['width'] / 2.0))
ballLeftBound = ballPosX - (ballRad * (2.0 / 3.0))
rightBound = (catcher['posX'] + (catcher['width'] / 2.0))
ballRightBound = ballPosX + (ballRad * (2.0 / 3.0))
#print(ballRightBound, rightBound)
if((ballRightBound < rightBound) and (ballLeftBound > leftBound)):
#print("within x range")
if ((ballLowerBound < lowerBound) and (ballLowerBound > upperBound)):
#print("touched catcher")
return True
return False
def updateBalls(ballElement):
#TODO: update all balls pos and other attributes
ballElement['posY'] += ballElement['speed']
ballElement['speed'] += ballElement['acceleration']
fill(ballElement['ballColor'])
circle(ballElement['posX'], ballElement['posY'], ballElement['rad'] * 2)
def detectInput():
global playerPressedKey
if keyPressed:
#if keyCode == LEFT and keyCode == RIGHT #Ummmm, Processing can't handle keys pressed at the same time properly
if keyCode == LEFT:
playerPressedKey = "leftPressed"
elif keyCode == RIGHT:
playerPressedKey = "rightPressed"
else:
playerPressedKey = "nothingPressed"
def updateCatcher():
#TODO: update catcher's pos
global catcher
detectInput()
#print("detectInput Runs!", playerPressedKey)
if playerPressedKey == "leftPressed":
catcher['curSpd'] -= catcher['acce']
#check if speed is higher than maxSpd
if abs(catcher['curSpd']) > catcher['maxSpd']:
catcher['curSpd'] = -1 * catcher['maxSpd']
elif playerPressedKey == "rightPressed":
catcher['curSpd'] += catcher['acce']
#check if speed is higher than maxSpd
if abs(catcher['curSpd']) > catcher['maxSpd']:
catcher['curSpd'] = catcher['maxSpd']
elif playerPressedKey == "nothingPressed":
if abs(catcher['curSpd']) > catcher['acce']:
if catcher['curSpd'] < 0:
catcher['curSpd'] += catcher['acce']
else:
catcher['curSpd'] -= catcher['acce']
else:
catcher['curSpd'] = 0
#Finally, update pos
catcher['posX'] += catcher['curSpd']
if (catcher['posX'] + catcher['width'] / 2.0) > screenResolutionWidth:
catcher['posX'] = screenResolutionWidth - (catcher['width'] / 2.0)
catcher['curSpd'] = 0
elif (catcher['posX'] - catcher['width'] / 2.0) < 0:
catcher['posX'] = catcher['width'] / 2.0
catcher['curSpd'] = 0
#print(catcher['curSpd'])
#draw catcher here
fill(catcher['color'])
rect(catcher['posX'], catcher['posY'], catcher['width'], catcher['height'], 0, 0, catcher['cornerRounding'], catcher['cornerRounding'])
def updateUI():
global displayedScore
#update displayedScorefirst!
if displayedScore < trueScore:
displayedScore += 1
textSize(30)
fill(122, 255, 211)
scoreText = ("Score: %i" % (displayedScore))
text(scoreText, 0, 30)
lifeRemainText = ("Life: %i" % (playerLifes))
fill(238, 255, 112)
text(lifeRemainText, 0, 60)
def drawScore():
#TODO: draw scoreboard here
global trueScore, displayedScore, highestScore
#re-initialize scores when game is over
drawScoreBoard()
if trueScore > highestScore:
highestScore = trueScore
gameInProgress = False
def drawScoreBoard():
drawColorfulBackground()
fill(10, 10, 10, 200)
rect(screenResolutionWidth / 2, screenResolutionHeight / 2, screenResolutionWidth - 60, screenResolutionHeight - 60, 10, 10, 10, 10)
highScoreText = ("Highest Score: %i" % (highestScore))
curScoreText = ("Your Score: %i" % (trueScore))
fill(255)
textSize(45)
text(highScoreText, 100, 300)
text(curScoreText, 100, 380)
drawInteractiveButton("Reset")
def setup():
size(screenResolutionWidth, screenResolutionHeight)
stroke(0)
frameRate(60) #framebased physics, yay
rectMode(CENTER)
def draw():
interfaceSelector()
Screenshot

| Status | Released |
| Author | bohdns |
| Genre | Platformer |
| Tags | 2D, Retro |
Download
Download
application.windows64.zip 42 MB

Leave a comment
Log in with itch.io to leave a comment.