Compare commits
No commits in common. "26f4c4826c4f24a1556c1d05c2f6f6d928c3a8b3" and "723bf797918a3168bb6c08430c149ec6e7927bbd" have entirely different histories.
26f4c4826c
...
723bf79791
12 changed files with 192 additions and 520 deletions
|
@ -1 +0,0 @@
|
|||
pyinstaller cli.py --onefile -y -n "rise to fall" --add-data="src\levels\testlevel.json;levels" --icon="src\icon.ico"
|
7
build.sh
7
build.sh
|
@ -1,7 +0,0 @@
|
|||
rm -rf "dist/rise to fall.app"
|
||||
rm -rf "dist/rise to fall"
|
||||
pyinstaller cli.py -y -w -n "rise to fall" --add-data="src/levels/testlevel.json:levels" --icon="src/icon.icns"
|
||||
codesign --remove-signature "dist/rise to fall/Python"
|
||||
codesign --remove-signature "dist/rise to fall.app/Contents/MacOS/Python"
|
||||
#codesign --remove-signature "dist/fall to rise"
|
||||
|
9
cli.py
9
cli.py
|
@ -1,9 +0,0 @@
|
|||
import sys, os, src.main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
||||
os.chdir(sys._MEIPASS)
|
||||
game = src.main.RiseToFall()
|
||||
game.run();
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 73 KiB |
17
readme.md
17
readme.md
|
@ -1,17 +0,0 @@
|
|||
# risetofall
|
||||
simple inertial platformer
|
||||
|
||||

|
||||
|
||||
controls:
|
||||
control | action
|
||||
---|---
|
||||
`w`|jump
|
||||
`a`|strafe left
|
||||
`d`|strafe right
|
||||
`space`|invert gravity
|
||||
|
||||
download:
|
||||
[latest release](https://github.com/Speedy6451/risetofall/releases/latest)
|
||||
|
||||
For platforms other than OS X, download source and run `cli.py`.
|
BIN
src/icon.icns
BIN
src/icon.icns
Binary file not shown.
BIN
src/icon.ico
BIN
src/icon.ico
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
BIN
src/icon.png
BIN
src/icon.png
Binary file not shown.
Before Width: | Height: | Size: 1 KiB |
BIN
src/icon.xcf
BIN
src/icon.xcf
Binary file not shown.
|
@ -1,240 +0,0 @@
|
|||
{
|
||||
"platforms": [
|
||||
{
|
||||
"pos1": [
|
||||
200,
|
||||
200
|
||||
],
|
||||
"pos2": [
|
||||
200,
|
||||
350
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
100,
|
||||
140
|
||||
],
|
||||
"pos2": [
|
||||
150,
|
||||
160
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
350,
|
||||
370
|
||||
],
|
||||
"pos2": [
|
||||
200,
|
||||
350
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
200,
|
||||
200
|
||||
],
|
||||
"pos2": [
|
||||
100,
|
||||
200
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
100,
|
||||
50
|
||||
],
|
||||
"pos2": [
|
||||
0,
|
||||
100
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
0,
|
||||
500
|
||||
],
|
||||
"pos2": [
|
||||
0,
|
||||
100
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
0,
|
||||
500
|
||||
],
|
||||
"pos2": [
|
||||
500,
|
||||
500
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
500,
|
||||
500
|
||||
],
|
||||
"pos2": [
|
||||
500,
|
||||
-100
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
0,
|
||||
-100
|
||||
],
|
||||
"pos2": [
|
||||
500,
|
||||
-100
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
-150,
|
||||
100
|
||||
],
|
||||
"pos2": [
|
||||
-50,
|
||||
1000
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
300,
|
||||
1000
|
||||
],
|
||||
"pos2": [
|
||||
-50,
|
||||
1000
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
350,
|
||||
1000
|
||||
],
|
||||
"pos2": [
|
||||
450,
|
||||
1000
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
750,
|
||||
1000
|
||||
],
|
||||
"pos2": [
|
||||
650,
|
||||
1000
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
870,
|
||||
900
|
||||
],
|
||||
"pos2": [
|
||||
900,
|
||||
850
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
1200,
|
||||
850
|
||||
],
|
||||
"pos2": [
|
||||
1000,
|
||||
850
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
1350,
|
||||
850
|
||||
],
|
||||
"pos2": [
|
||||
1900,
|
||||
850
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
}
|
||||
|
||||
],
|
||||
"obstacles": [
|
||||
{
|
||||
"pos": [
|
||||
300,300
|
||||
]
|
||||
}
|
||||
],
|
||||
"playerSpawn": [200,400],
|
||||
"checkpoints": [
|
||||
{
|
||||
"pos": [20,900]
|
||||
}
|
||||
],
|
||||
"switches": [
|
||||
{
|
||||
"pos": [1150,800]
|
||||
}
|
||||
],
|
||||
"text": [
|
||||
{
|
||||
"text": "a and d to move",
|
||||
"pos": [20,420],
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"text" : "w to jump",
|
||||
"pos" : [380,420],
|
||||
"color" : ""
|
||||
},
|
||||
{
|
||||
"text" : "jump on box for checkpoint",
|
||||
"pos" : [50,900],
|
||||
"color" : ""
|
||||
},
|
||||
{
|
||||
"text" : "congratulations, you made it to the end",
|
||||
"pos" : [1350,800],
|
||||
"color" : ""
|
||||
}
|
||||
|
||||
],
|
||||
"upperLimit": 1500,
|
||||
"lowerLimit": -500
|
||||
}
|
383
src/main.py
383
src/main.py
|
@ -4,60 +4,35 @@ import pymunk, pymunk.pygame_util, pygame, random, json
|
|||
|
||||
class Camera(object):
|
||||
"""
|
||||
This class implements a simple camera that can pan through the level and track an object
|
||||
This class implements a simple camera that can pan through the level
|
||||
"""
|
||||
def __init__(self, x = 0, y = 0, w=400, h = 600) -> None:
|
||||
self.x = x # cam x offset
|
||||
self.y = y # cam y offset
|
||||
self.w = w # cam width
|
||||
self.h = h # cam height
|
||||
self.upperLimit = None # max cam y
|
||||
self.lowerLimit = None # min cam y
|
||||
self.tracking = None # tracking target
|
||||
self.speed = 0.1 # tracking speed (get 5% closer to target every frame)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = w
|
||||
self.h = h
|
||||
self.tracking = None
|
||||
|
||||
def translate(self, pos):
|
||||
"""
|
||||
This function translates the given coordinate to give a camera pan effect
|
||||
"""
|
||||
return (int(pos[0]-self.x),int(pos[1]-self.y))
|
||||
|
||||
def setBounds(self, upperLimit, lowerLimit):
|
||||
self.upperLimit = upperLimit
|
||||
self.lowerLimit = lowerLimit
|
||||
return (pos[0]-self.x,pos[1]-self.y)
|
||||
|
||||
def outOfBounds(self, pos):
|
||||
"""
|
||||
This function checks if the given coordinate is currently visible. It is used to despawn objects
|
||||
"""
|
||||
if (pos[0]-self.y >self.h or pos[0]-self.y < 0
|
||||
if (pos[0]-self.y >self.h or pos[0]-self.y < 0
|
||||
or pos[1]-self.x >self.w or pos[1]-self.x < 0):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def track(self, thing):
|
||||
"""
|
||||
This function sets the tracking target
|
||||
r """
|
||||
self.tracking = thing
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
This function is called every frame, and pans toward the given target.
|
||||
"""
|
||||
if self.tracking:
|
||||
target = self.tracking.getPos()
|
||||
target = (target[0]-(self.w/2),target[1]-(self.h/2)) # find offset from target
|
||||
target = (target[0]-(self.w/2),target[1]-(self.h/2))
|
||||
|
||||
self.x += (target[0]-self.x)*self.speed # pan toward target at given speed
|
||||
self.y += (target[1]-self.y)*self.speed
|
||||
|
||||
# constrain camera to limits TODO fix, is broken
|
||||
if self.upperLimit:
|
||||
self.y = min(self.upperLimit+(self.h/2), self.y)
|
||||
if self.lowerLimit:
|
||||
self.y = max(self.lowerLimit-(self.h/2), self.y)
|
||||
self.x += (target[0]-self.x)/2
|
||||
self.y += (target[1]-self.y)/2
|
||||
|
||||
class Thing(object):
|
||||
"""
|
||||
|
@ -67,74 +42,32 @@ class Thing(object):
|
|||
self._mass = mass # weight
|
||||
self._moment = moment # rotational inertia
|
||||
self._bodyType = bodyType # fixed/moving
|
||||
self._body = pymunk.Body(self._mass,self._moment,self._bodyType) # body (physics object)
|
||||
self._body.position = (400, 0) # position
|
||||
self._body = pymunk.Body(self._mass,self._moment,self._bodyType)
|
||||
self._body.position = (400, 0)
|
||||
self.visible = True
|
||||
self._game = game # game object
|
||||
|
||||
def setPos(self, x,y):
|
||||
"""
|
||||
This function sets the object's position given an x and y coordinate
|
||||
"""
|
||||
self._body.position = (x,y)
|
||||
|
||||
def setPosList(self, pos):
|
||||
"""
|
||||
This function sets the object's position given a coordinate list
|
||||
"""
|
||||
self._body.position = pos
|
||||
|
||||
def getPos(self):
|
||||
"""
|
||||
This function returns the current position as a list
|
||||
"""
|
||||
return (self._body.position)
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function draws the object
|
||||
It is empty by default as a Thing object has no visual form
|
||||
"""
|
||||
pass
|
||||
|
||||
class Text(object):
|
||||
def __init__(self,game,text = 'test',posList = (0,0), color = (0,0,0,1)):
|
||||
self._game = game
|
||||
self.text = text
|
||||
self.pos = posList
|
||||
self.color = color
|
||||
self.img = game.font.render(text,True,color)
|
||||
def draw(self):
|
||||
self._game._screen.blit(self.img,self._game.camera.translate(self.pos))
|
||||
|
||||
class Checkpoint(object):
|
||||
def __init__(self,game, pos = (0,0)):
|
||||
self._game = game
|
||||
self.pos = pos
|
||||
def draw(self):
|
||||
playPos = self._game.player.getPos()
|
||||
if playPos[0] > self.pos[0] and playPos[0] < self.pos[0] + 25 and playPos[1] > self.pos[1] and playPos[1] < self.pos[1] + 25:
|
||||
print("checkpoint")
|
||||
self._game.level.level['playerSpawn'] = self.pos
|
||||
pygame.draw.rect(self._game._screen,(0,255,0,.5),pygame.Rect(self._game.camera.translate(self.pos),(25,25)))
|
||||
|
||||
class Circle(Thing):
|
||||
"""
|
||||
This class implements a pymunk circle that can be displayed on a pygame window
|
||||
"""
|
||||
def __init__(self, game, radius = 80, color = pygame.Color(0,0,255,1), mass = 1, moment = 1666, bodyType = pymunk.Body.DYNAMIC, friction = 0, elasticty = 0.05) -> None:
|
||||
def __init__(self, game, radius = 80, color = pygame.Color(0,0,255,1), mass = 1, moment = 1666, bodyType = pymunk.Body.DYNAMIC) -> None:
|
||||
super().__init__(game, mass=mass, moment=moment, bodyType=bodyType)
|
||||
self.radius = radius # circle radius
|
||||
self.color = color # circle color
|
||||
self._poly = pymunk.Circle(self._body,radius) # physics shape
|
||||
self._poly.friction = friction
|
||||
self._poly.elasticty = elasticty
|
||||
self._game._space.add(self._body,self._poly) # add self to physics simulation
|
||||
self.radius = radius
|
||||
self.color = color
|
||||
self._poly = pymunk.Circle(self._body,radius)
|
||||
self._game._space.add(self._body,self._poly)
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function draws the circle every frame
|
||||
"""
|
||||
if self.visible:
|
||||
pygame.draw.circle(self._game._screen,self.color,self._game.camera.translate(self._body.position),self.radius)
|
||||
|
||||
|
@ -142,105 +75,87 @@ class Platform(Thing):
|
|||
"""
|
||||
This class implements a static platform
|
||||
"""
|
||||
def __init__(self, game, pos1 = (20,40), pos2 = (40,40), width = 5, color = pygame.Color(0,0,0,1, friction = .9, elasticty = 0.8)) -> None:
|
||||
def __init__(self, game, pos1 = (20,40), pos2 = (40,40), width = 5, color = pygame.Color(0,0,0,1)) -> None:
|
||||
super().__init__(game, bodyType=pymunk.Body.DYNAMIC)
|
||||
self._body = pymunk.Body(body_type=pymunk.Body.STATIC) # Creates static body (unmovable)
|
||||
self.color = color # line color
|
||||
self._pos1 = pos1 # line point 1
|
||||
self._pos2 = pos2 # line point 2
|
||||
self._width = width # line width
|
||||
self._poly = pymunk.Segment(self._body,pos1,pos2,width) # create physics shape
|
||||
self._poly.friction = 0.6 #TODO figure out why this will not set from var
|
||||
self._poly.elasticty = 0.0
|
||||
self._game._space.add(self._body,self._poly) # add self to simulation
|
||||
self._body = pymunk.Body(body_type=pymunk.Body.STATIC)
|
||||
self.color = color
|
||||
self._pos1 = pos1
|
||||
self._pos2 = pos2
|
||||
self._width = width
|
||||
self._poly = pymunk.Segment(self._body,pos1,pos2,width)
|
||||
self._game._space.add(self._body,self._poly)
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function draws the platform every frame
|
||||
"""
|
||||
if self.visible:
|
||||
pygame.draw.line(self._game._screen, self.color, self._game.camera.translate(self._pos1), self._game.camera.translate(self._pos2), self._width)
|
||||
|
||||
class RainDrop(Thing):
|
||||
"""
|
||||
Raindrop class
|
||||
Raindrop
|
||||
"""
|
||||
def __init__(self,game, maxTime):
|
||||
super().__init__(game, 0.02) # mass of 0.02
|
||||
self.radius = 2 # raindrop radius 2px
|
||||
self.color = pygame.Color(0,0,255,1) # raindrop color blue
|
||||
self._poly = pymunk.Circle(self._body,self.radius) # create physics object
|
||||
self._maxTime = maxTime # death timer
|
||||
self._startTime = pygame.time.get_ticks() # creation time
|
||||
self.destroy = False # death flag
|
||||
self._game._space.add(self._body,self._poly) # add self to simulation
|
||||
self._body.position = self._game.camera.translate((random.randint(0,self._game.windowSize[0]),0)) # spawn randomly at top of screen
|
||||
self._body.apply_force_at_local_point((0,self._game._space.gravity[1]*1)) # push raindrop downward
|
||||
self._poly.collision_type = 4 # set type so as not to interact with the player
|
||||
super().__init__(game, 0.02)
|
||||
self.radius = 2
|
||||
self.color = pygame.Color(0,0,255,1)
|
||||
self._poly = pymunk.Circle(self._body,self.radius)
|
||||
self._maxTime = maxTime
|
||||
self._startTime = pygame.time.get_ticks()
|
||||
self.destroy = False
|
||||
self._game._space.add(self._body,self._poly)
|
||||
self._body.position = self._game.camera.translate((random.randint(0,self._game.windowSize[0]),0))
|
||||
self._body.apply_force_at_local_point((0,self._game._space.gravity[1]*1))
|
||||
self._poly.collision_type = 4
|
||||
def update(self):
|
||||
"""
|
||||
This function is called every frame and checks if the raindrop should die because it is out of bounds, or it is too old.
|
||||
"""
|
||||
t = pygame.time.get_ticks()
|
||||
dt= t - self._startTime # calculate delta from creation
|
||||
if (dt > self._maxTime): # kill if too old
|
||||
dt= t - self._startTime
|
||||
if (dt > self._maxTime):
|
||||
self.destroy = True
|
||||
|
||||
if self._game.camera.outOfBounds(self.getPos()): # kill if offscreen
|
||||
if self._game.camera.outOfBounds(self.getPos()):
|
||||
self.destroy = True
|
||||
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function draws the raindrop each frame
|
||||
"""
|
||||
if self.visible:
|
||||
pygame.draw.circle(self._game._screen,self.color,self._game.camera.translate(self._body.position),self.radius)
|
||||
def delete(self):
|
||||
"""
|
||||
This function removes the raindrop from the physics simulation
|
||||
"""
|
||||
self._game._space.remove(self._body,self._poly)
|
||||
|
||||
class Rain(object):
|
||||
def rainCollisionHandler(self, arbiter, space, data):
|
||||
"""
|
||||
This function activates when rain hits the player and ensures they do not interact
|
||||
"""
|
||||
print('thing')
|
||||
return False
|
||||
|
||||
def __init__(self,game,maxDrops = 10,maxTime = 15000, dropRate=400):
|
||||
self.dropList = [RainDrop(game, maxTime)] # list of raindrops
|
||||
self.lastSpawn = pygame.time.get_ticks() # time last raindrop was spawned
|
||||
self.maxDrops = maxDrops # maximum raindrops at a time
|
||||
self.maxTime = maxTime # maximun time a raindrop can exist
|
||||
self.dropRate = dropRate # milliseconds between raindrops
|
||||
self.game = game # game object
|
||||
self.raining = True # toggle new rain
|
||||
self.dropList = [RainDrop(game, maxTime)]
|
||||
self.lastSpawn = pygame.time.get_ticks()
|
||||
self.maxDrops = maxDrops
|
||||
self.maxTime = maxTime
|
||||
self.dropRate = dropRate
|
||||
self.game = game
|
||||
self.raining = True
|
||||
|
||||
self.collisionHandler = game._space.add_collision_handler(3,4) # keeps raindrops from interacting with the player
|
||||
self.collisionHandler = game._space.add_collision_handler(3,4)
|
||||
self.collisionHandler.begin = self.rainCollisionHandler
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
This function is called every frame and is what creates and destroys raindrops
|
||||
"""
|
||||
t = pygame.time.get_ticks()
|
||||
dt= t - self.lastSpawn # delta since last drop spawned
|
||||
dt= t - self.lastSpawn
|
||||
|
||||
if self.raining:
|
||||
if (len(self.dropList) < self.maxDrops and dt > self.dropRate): # create a raindrop if there is room, and the time has passed
|
||||
self.dropList.append(RainDrop(self.game, self.maxTime)) # create drop
|
||||
self.lastSpawn = t # reset counter
|
||||
if (len(self.dropList) < self.maxDrops and dt > self.dropRate):
|
||||
self.dropList.append(RainDrop(self.game, self.maxTime))
|
||||
self.lastSpawn = t
|
||||
|
||||
|
||||
for raindrop in self.dropList:
|
||||
raindrop.update() # make drops check if they should die
|
||||
if (raindrop.destroy == True): # delete drops whose time has come
|
||||
raindrop.update()
|
||||
if (raindrop.destroy == True):
|
||||
self.dropList.remove(raindrop)
|
||||
raindrop.delete()
|
||||
del raindrop
|
||||
else:
|
||||
raindrop.draw() # draw all alive drops
|
||||
raindrop.draw()
|
||||
|
||||
class Level(object):
|
||||
"""
|
||||
|
@ -248,36 +163,15 @@ class Level(object):
|
|||
Creates and destroys a level from a JSON file.
|
||||
"""
|
||||
|
||||
def platformCollisionHandler(self, arbiter, space, data): # allow player to jump again on collision with platform
|
||||
self._game.player.canJump = True
|
||||
return True # make player collide
|
||||
|
||||
def __init__(self, levelFilePath: str, game):
|
||||
levelf = open(levelFilePath)
|
||||
leveltxt = levelf.read()
|
||||
levelf.close()
|
||||
self.level = json.loads(leveltxt)
|
||||
#self.level = json.loads('{ "platforms": [ { "pos1": [ 200, 200 ], "pos2": [ 200, 350 ], "width": 4, "color": "" }, { "pos1": [ 100, 140 ], "pos2": [ 150, 160 ], "width": 4, "color": "" }, { "pos1": [ 350, 370 ], "pos2": [ 200, 350 ], "width": 4, "color": "" }, { "pos1": [ 200, 200 ], "pos2": [ 100, 200 ], "width": 4, "color": "" }, { "pos1": [ 100, 50 ], "pos2": [ 0, 100 ], "width": 4, "color": "" }, { "pos1": [ 0, 500 ], "pos2": [ 0, 100 ], "width": 4, "color": "" }, { "pos1": [ 0, 500 ], "pos2": [ 500, 500 ], "width": 4, "color": "" }, { "pos1": [ 500, 500 ], "pos2": [ 500, -100 ], "width": 4, "color": "" }, { "pos1": [ 0, -100 ], "pos2": [ 500, -100 ], "width": 4, "color": "" }, { "pos1": [ -150, 100 ], "pos2": [ -50, 1000 ], "width": 4, "color": "" }, { "pos1": [ 300, 1000 ], "pos2": [ -50, 1000 ], "width": 4, "color": "" }, { "pos1": [ 350, 1000 ], "pos2": [ 450, 1000 ], "width": 4, "color": "" }, { "pos1": [ 750, 1000 ], "pos2": [ 650, 1000 ], "width": 4, "color": "" }, { "pos1": [ 870, 900 ], "pos2": [ 900, 850 ], "width": 4, "color": "" }, { "pos1": [ 1200, 850 ], "pos2": [ 1000, 850 ], "width": 4, "color": "" }, { "pos1": [ 1350, 850 ], "pos2": [ 1900, 850 ], "width": 4, "color": "" } ], "obstacles": [ { "pos": [ 300,300 ] } ], "playerSpawn": [200,400], "checkpoints": [ { "pos": [20,900] } ], "switches": [ { "pos": [1150,800] } ], "text": [ { "text": "a and d to move", "pos": [20,420], "color": "" }, { "text" : "w to jump", "pos" : [380,420], "color" : "" }, { "text" : "jump on box for checkpoint", "pos" : [50,900], "color" : "" }, { "text" : "congratulations, you made it to the end", "pos" : [1350,800], "color" : "" }], "upperLimit": 1500,"lowerLimit":-500 }')
|
||||
self._game = game # game object
|
||||
|
||||
self.collisionHandler = game._space.add_collision_handler(3,5) # allow player to jump on collision
|
||||
self.collisionHandler.begin = self.platformCollisionHandler
|
||||
self._levelFilePath = levelFilePath
|
||||
self._levelFile = open(self._levelFilePath, 'r')
|
||||
self.level = json.load(self._levelFile)
|
||||
self._game = game
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
This function starts the level, and spawns in all level objects
|
||||
"""
|
||||
for platform in self.level['platforms']:
|
||||
box = Platform(self._game,platform['pos1'],platform['pos2'],platform['width'] or 4,platform['color'] or (0,0,0,1)) # create platform object based on data
|
||||
box._poly.collision_type = 5 # specify as a platform collision type
|
||||
self._game._shapes.append(box) # add object to the game
|
||||
for text in self.level['text']:
|
||||
textObj = Text(self._game,text['text'],text['pos'],text['color'] or (0,0,0,1))
|
||||
self._game._shapes.append(textObj)
|
||||
for checkpoint in self.level['checkpoints']:
|
||||
check = Checkpoint(self._game,checkpoint['pos'])
|
||||
self._game._shapes.append(check)
|
||||
|
||||
self._game._shapes.append(Platform(self._game,platform['pos1'],platform['pos2'],platform['width'] or 4,platform['color'] or (0,0,0,1)))
|
||||
|
||||
class Player(Circle):
|
||||
"""
|
||||
|
@ -285,33 +179,23 @@ class Player(Circle):
|
|||
implements the player that can move and jump
|
||||
"""
|
||||
def __init__(self, game):
|
||||
super().__init__(game, 13, (255,0,0,1), friction = .2, elasticty = 0.0)
|
||||
self._poly.collision_type = 3 # specify to collide as a player
|
||||
self.moveRight = False # is d pressed
|
||||
self.moveLeft = False # is a pressed
|
||||
self.setPosList(self._game.level.level['playerSpawn']) # set position to level start
|
||||
self.canJump = True # can the player jump
|
||||
super().__init__(game, 13, (255,0,0,1))
|
||||
self.setPos(random.randint(0,self._game.windowSize[0]),0)
|
||||
self._poly.collision_type = 3
|
||||
self.moveRight = False
|
||||
self.moveLeft = False
|
||||
|
||||
def jump(self):
|
||||
"""
|
||||
This function makes the player jump if it can
|
||||
"""
|
||||
if self.canJump: # if the player can jump
|
||||
self._body.apply_force_at_world_point((0,-15000), (self.getPos()[0],self.getPos()[1])) # shove the player up. uses world point to apply force in same direction regardless of direction
|
||||
self.canJump = False # the player can no longer jump
|
||||
self._body.apply_force_at_local_point((0,-15000))
|
||||
print('jump')
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function is called every frame and draws the player
|
||||
"""
|
||||
super().draw()
|
||||
if self.getPos()[1] > self._game.level.level['upperLimit'] or self.getPos()[1] < self._game.level.level['lowerLimit']: # respawn the player if fallen out of map
|
||||
self._body._set_velocity([0,0]) # zero player velocity
|
||||
self.setPosList(self._game.level.level['playerSpawn']) # go to spawnpoint
|
||||
if self.moveLeft: # is a pressed
|
||||
self._body.apply_force_at_world_point((-200,0), (self.getPos()[0],self.getPos()[1])) # shove player left
|
||||
if self.moveRight: # is d pressed
|
||||
self._body.apply_force_at_world_point((200,0), (self.getPos()[0],self.getPos()[1])) # shove player right
|
||||
if self.moveLeft:
|
||||
self._body.apply_force_at_local_point((-200,0))
|
||||
if self.moveRight:
|
||||
self._body.apply_force_at_local_point((200,0))
|
||||
|
||||
|
||||
class Mouse(Thing):
|
||||
"""
|
||||
|
@ -320,17 +204,15 @@ class Mouse(Thing):
|
|||
"""
|
||||
def __init__(self,game):
|
||||
super().__init__(game)
|
||||
self._body = pymunk.Body(body_type=pymunk.Body.STATIC) # unaffected by gravity
|
||||
self._poly = pymunk.Circle(self._body,6) # create physics shape
|
||||
self._game._space.add(self._body,self._poly) # add to simulation
|
||||
self._body = pymunk.Body(body_type=pymunk.Body.STATIC)
|
||||
self._poly = pymunk.Circle(self._body,60)
|
||||
self._game._space.add(self._body,self._poly)
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
This function is called every frame and draws the mouse to the screen. It also ensures the mouse is on the mouse
|
||||
"""
|
||||
mouse = pygame.mouse.get_pos() # get mouse position
|
||||
self.setPosList(mouse) # go to mouse
|
||||
pygame.draw.circle(self._game._screen,(0,0,0,1),(int(self._body.position[0]),int(self._body.position[1])),6)
|
||||
mouse = pygame.mouse.get_pos()
|
||||
print(mouse)
|
||||
self.setPos(mouse[0],mouse[1])
|
||||
pygame.draw.circle(self._game._screen,(0,0,0,1),self._body.position,6)
|
||||
|
||||
class RiseToFall(object):
|
||||
"""
|
||||
|
@ -339,92 +221,101 @@ class RiseToFall(object):
|
|||
|
||||
def __init__(self) -> None:
|
||||
# create space
|
||||
self._space = pymunk.Space() # physics arena
|
||||
self._space.gravity = 0,300 # gravity
|
||||
self.windowSize = (400,600) # size of window w,h
|
||||
self._space = pymunk.Space()
|
||||
self._space.gravity = 0,300
|
||||
self.windowSize = (400,600)
|
||||
|
||||
# initialize pygame
|
||||
pygame.init()
|
||||
self._screen = pygame.display.set_mode(self.windowSize, pygame.RESIZABLE) # set window size and enable resizing
|
||||
self._screen = pygame.display.set_mode(self.windowSize, pygame.RESIZABLE)
|
||||
self._clock = pygame.time.Clock()
|
||||
|
||||
self.clock = pygame.time.Clock()
|
||||
|
||||
# set caption
|
||||
pygame.display.set_caption("rise to fall")
|
||||
|
||||
self._running = True
|
||||
|
||||
self._shapes = [] # list of objects
|
||||
self._shapes = []
|
||||
|
||||
self.font = pygame.font.SysFont("Arial.ttf",36) # font
|
||||
box = Platform(self, (100,140), (150,160))
|
||||
self._shapes.append(box)
|
||||
# font = pygame.font.SysFont("Arial",16)
|
||||
|
||||
|
||||
self.camera = Camera(w=self.windowSize[0],h=self.windowSize[1]) # initialize camera
|
||||
|
||||
self.rain = Rain(self) # initialize rain
|
||||
|
||||
#self._shapes.append(Mouse(self)) # initialize mouse
|
||||
self.camera = Camera(w=self.windowSize[0],h=self.windowSize[1])
|
||||
|
||||
|
||||
# load and start the first level
|
||||
self.level = Level('src/levels/testlevel.json',self)
|
||||
self.level.start()
|
||||
self.camera.setBounds(self.level.level['upperLimit'],self.level.level['lowerLimit'])
|
||||
self.rain = Rain(self)
|
||||
|
||||
self._shapes.append(Mouse(self))
|
||||
|
||||
# create player
|
||||
self.player = Player(self)
|
||||
self.camera.track(self.player)
|
||||
self._shapes.append(self.player)
|
||||
self.camera.track(self.player)
|
||||
|
||||
self.level = Level('testlevel.json',self)
|
||||
self.level.start()
|
||||
|
||||
# set draw options
|
||||
self._print_options = pymunk.pygame_util.DrawOptions(self._screen)
|
||||
|
||||
def _createBox(self):
|
||||
body = pymunk.Body(1,1666)
|
||||
body.position = 50,100
|
||||
|
||||
poly = pymunk.Poly.create_box(body)
|
||||
|
||||
self._space.add(body, poly)
|
||||
|
||||
def _processEvents(self):
|
||||
"""
|
||||
This function is called every frame and handles events such as keyboard input
|
||||
"""
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT: # close button on window
|
||||
if event.type == pygame.QUIT:
|
||||
self._running = False
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: # exit if escape pressed
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
||||
self._running = False
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: # invert gravity on spacebar
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
|
||||
#self._createBox()
|
||||
shape = Circle(self, 10)
|
||||
shape.setPos(random.randint(0,400),0)
|
||||
shape.color = pygame.Color(255,0,255,1)
|
||||
self._shapes.append(shape)
|
||||
self._space.gravity = (self._space.gravity[0]*-1,self._space.gravity[1]*-1)
|
||||
self.rain.raining = not self.rain.raining
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_a and self.player: # begin moving left
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_a and self.player:
|
||||
self.player.moveLeft = True
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_d and self.player: # begin moving right
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_d and self.player:
|
||||
self.player.moveRight = True
|
||||
if event.type == pygame.KEYUP and event.key == pygame.K_a and self.player: # stop moving left
|
||||
if event.type == pygame.KEYUP and event.key == pygame.K_a and self.player:
|
||||
self.player.moveLeft = False
|
||||
if event.type == pygame.KEYUP and event.key == pygame.K_d and self.player: # stop moving right
|
||||
if event.type == pygame.KEYUP and event.key == pygame.K_d and self.player:
|
||||
self.player.moveRight = False
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_w and self.player: # jump
|
||||
if event.type == pygame.KEYDOWN and event.key == pygame.K_w and self.player:
|
||||
self.player.jump()
|
||||
if event.type == pygame.VIDEORESIZE: # resize if window moved
|
||||
if event.type == pygame.VIDEORESIZE:
|
||||
self._screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)
|
||||
self.camera.h = event.h
|
||||
self.camera.w = event.w
|
||||
|
||||
|
||||
|
||||
def _drawEntities(self):
|
||||
"""
|
||||
This function is called every frame and draws all objects
|
||||
"""
|
||||
for shape in self._shapes:
|
||||
shape.draw()
|
||||
self.player.draw()
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Main game loop
|
||||
"""
|
||||
while self._running:
|
||||
self._processEvents() # process keyboard input
|
||||
self.camera.update() # make camera pan
|
||||
self._screen.fill(pygame.Color("white")) # clear screen
|
||||
#self.rain.update() # process rain TODO: remove or fix
|
||||
self._drawEntities() # draw entities
|
||||
pygame.display.flip() # write to screen
|
||||
self._space.step(0.02) # increment simulation
|
||||
self.clock.tick(60)
|
||||
self._processEvents();
|
||||
self.camera.update()
|
||||
self._space.step(0.02)
|
||||
self._screen.fill(pygame.Color("white"))
|
||||
# self._space.debug_draw(self._print_options)
|
||||
self.rain.update()
|
||||
self._drawEntities()
|
||||
pygame.display.flip()
|
||||
|
||||
|
||||
if __name__ == "__main__": # run game if called directly
|
||||
if __name__ == "__main__":
|
||||
game = RiseToFall()
|
||||
game._createBox()
|
||||
game.run()
|
||||
|
|
55
src/testlevel.json
Normal file
55
src/testlevel.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"platforms": [
|
||||
{
|
||||
"pos1": [
|
||||
200,
|
||||
200
|
||||
],
|
||||
"pos2": [
|
||||
200,
|
||||
350
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"pos1": [
|
||||
200,
|
||||
350
|
||||
],
|
||||
"pos2": [
|
||||
300,
|
||||
350
|
||||
],
|
||||
"width": 4,
|
||||
"color": ""
|
||||
}
|
||||
],
|
||||
"obstacles": [
|
||||
{
|
||||
"pos": [
|
||||
300,300
|
||||
]
|
||||
}
|
||||
],
|
||||
"playerSpawn": [200,400],
|
||||
"checkpoints": [
|
||||
{
|
||||
"pos": [300,300],
|
||||
"size":[20,20]
|
||||
}
|
||||
],
|
||||
"switches": [
|
||||
{
|
||||
"pos": [300,300],
|
||||
"rotation": 0
|
||||
}
|
||||
],
|
||||
"text": [
|
||||
{
|
||||
"text": "a and d to move",
|
||||
"pos": [100,100],
|
||||
"size": 14
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue