1
0
forked from Rowland/EG
EG/RenderPipelineFile/samples/05-Quboid/src/Cube.py
2025-07-24 11:37:46 +08:00

379 lines
15 KiB
Python

from direct.interval.LerpInterval import LerpPosInterval, LerpHprInterval, LerpScaleInterval
from direct.interval.LerpInterval import LerpPosHprInterval,LerpColorScaleInterval, LerpFunc
from direct.interval.MetaInterval import Sequence,Parallel
from direct.interval.FunctionInterval import Func,Wait
from direct.task.Task import Task
from panda3d.core import Vec3
from functools import partial
from random import random
from .CheckMove import *
from .Sound import *
class Cube:
def __init__(self,level):
self.level = level
self.sounds = Sounds()
self.cube = loader.loadModel("./models/Cuboid.bam")
self.cube.setPythonTag("TilePos", [0,0,None,None] ) #tells us which 2 tiles the block is one, x1,y1,x2,y2.
#x2 and y2 are NONE if it's standing upright
self.cube.setPythonTag("Animated", False) # i so dont want to pass around global parameters
self.cube.reparentTo(render)
self.moves = 0
self.falls = 0
self.shard_node = render.attach_new_node("shards")
def resetStats(self):
self.moves = 0
self.falls = 0
def enableGameControl(self):
"""
assigns the keyboard-events to the block's movement
"""
base.accept("arrow_up", self.rotateCube, [ "up" ])
base.accept("arrow_down", self.rotateCube, [ "down" ])
base.accept("arrow_left", self.rotateCube, [ "left" ])
base.accept("arrow_right", self.rotateCube, [ "right" ])
def disableGameControl(self):
"""
clears the keyboard-events so the keys can be used to navigate the menu or other stuff.
"""
base.ignore("arrow_up")
base.ignore("arrow_down")
base.ignore("arrow_left")
base.ignore("arrow_right")
def setPos(self, x1,y1=None ):
"""
sets the block to the position you desire.
you can either throw in 2 integers which make the tile positions. or you pass a tuple/list with at least 2 items.
in both cases the it is x y coordinates. the block will be roated into a standing position if it isn't already.
"""
if type(x1) == list or type(x1) == tuple:
y1=x1[1]
x1=x1[0]
if self.getCubeTiles()[3]:
self.cube.setHpr(render,0,0,0)
self.cube.setPos(render,x1,y1,1)
self.setCubeTiles(x1,y1)
def getCube(self):
"""
returns the cube's node path.
"""
return self.cube
def getCubeTiles(self):
"""
internal convenience functions. returns a list with [x1,y1,x2,y2]
"""
return self.cube.getPythonTag("TilePos")
def setCubeTiles(self,x1,y1,x2=None,y2=None):
"""
internal convenience-function which sets the tile coordinates for the tiles the block is on. inputs are x1,y1,x2,y2 . x2 and y2 are optional.
"""
self.cube.setPythonTag("TilePos", [x1,y1,x2,y2] )
def isAnimated(self):
"""
returns True if the block is currently playing an animation.. DONT TOUCH IT while it is animated or it will turn into evil-rotation cube.
"""
return self.cube.getPythonTag("Animated")
def setAnimated(self,param):
"""
internal function: sets and clears the "isAnimated" tag
"""
self.cube.setPythonTag("Animated", param)
def fadeInCube(self,duration = 2.5):
"""
helper function. simply moves the block from above onto the map. using the duration specified as parameter.
if no parameter is supplied, it will assume the default-time of 1.2 seconds or so.
"""
self.setAnimated(True)
x1,y1,x2,y2 = self.cube.getPythonTag("TilePos")
#animate the cube, once finished clear the animation tag
self.cube.setZ(render,15)
self.cube.wrtReparentTo(render)
print("Starting fade in sequence")
Sequence( LerpPosInterval(self.cube, duration ,(x1, y1,1), blendType="easeOut" ) ,
Func(self.setAnimated,False),
Func(lambda:self.level.stopAnimatedTile(x1,y1) ) ).start()
def animate(self,myInterval):
"""
internal function which accepts an interval as input, plays it. and automatically resets the "animated" tag once it finished.
you have to check yourself if the tag is set before playing the animation. use isAnimated() for that.
"""
#print "animating cube"
seq = Sequence( myInterval , Func(self.setAnimated,False) )
#seq.start()
return seq
def resetCube(self,task=None):
"""
resets the cube to the statring position, including the fly-in-from-above animation.
"""
print("Resetting cube")
x,y,z = self.level.getPosFromTile(self.level.getStartTile())
self.setPos(x,y)
self.cube.setHpr(render,0,0,0)
#self.level.stopAnimatedTile(x,y )
print("Fading in cube")
self.fadeInCube()
def levelUp(self, task=None):
"""
calls the levelClass to load the next level and resets the cube.
"""
if self.level.loadLevel() == 0: #if level loaded successfully...
self.resetCube()
self.level.main.gui.set_stage(self.level.LevelNr + 1)
def rotateCube(self,direction):
"""
rotates the cube according to the direction passed as first argument. accepted values are "up" "down" "left" "right"
"""
#first check if we are allowed to move the block at all..
if self.isAnimated() :
print("canceling rotation of cube")
return
#cleaning up from last rotation (we cant clean those up at the end of this function cause the interval needs the dummynode for rotation)
self.cube.wrtReparentTo(render)
try:
dummy.remove_node()
except:
pass
self.setAnimated(True)
duration = 0.2
x1,y1,x2,y2 = self.getCubeTiles()
self.level.animateTile(x1,y1)
self.level.animateTile(x2,y2)
dummy = render.attachNewNode("dummy")
dummy.reparentTo(self.cube)
dummy.setZ(render,0)
dummy.wrtReparentTo(render)
dummy.setHpr(0,0,0)
dest_hpr = Vec3(0)
if self.cube.getZ(render) > .7:
#case1 : cube is standing upright
#ok... since we rotate relative there are rounding errors... since there !might! be some uebernoob playing the game
#needing one gazillion rotations to reach the goal it might happen those rounding errors actually get visible
#so let him enjoy and reset the cube every-time it's standing straight-up.
self.cube.setZ(render,1) #how comes this is one?.. well.. i know.. because of my learnings..
self.cube.setX(render, round(self.cube.getX(render),0) )
self.cube.setY(render, round(self.cube.getY(render),0) )
self.cube.setH(render, round(self.cube.getH(render),0) )
self.cube.setP(render, round(self.cube.getP(render),0) )
self.cube.setR(render, round(self.cube.getR(render),0) )
if direction == "right":
dummy.setY(dummy,.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, -90, 0)
self.setCubeTiles( x1, y1+1 ,x1 , y1+2 )
if direction == "left":
dummy.setY(dummy,-.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 90, 0)
self.setCubeTiles( x1, y1-2 ,x1 , y1-1 )
if direction == "up":
dummy.setX(dummy,-.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, -90)
self.setCubeTiles( x1-2, y1 ,x1-1 , y1 )
if direction == "down":
dummy.setX(dummy,.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, 90)
self.setCubeTiles( x1+1, y1 ,x1+2 , y1 )
elif x1 == x2 : #if aligned to y-axis
if direction == "right":
dummy.setY(dummy,1)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, -90, 0)
self.setCubeTiles( x1, y1+2 )
if direction == "left":
dummy.setY(dummy,-1)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 90, 0)
self.setCubeTiles( x1, y1-1)
if direction == "up":
dummy.setX(dummy,-.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, -90)
self.setCubeTiles( x1-1, y1, x2-1, y2 )
if direction == "down":
dummy.setX(dummy,.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, 90)
self.setCubeTiles( x1+1, y1, x2+1, y2 )
elif y1==y2 : #if it is alligned to x-axis..
if direction == "right":
dummy.setY(dummy,.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, -90, 0)
self.setCubeTiles( x1, y1+1, x2, y2+1 )
if direction == "left":
dummy.setY(dummy,-.5)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 90, 0)
self.setCubeTiles( x1, y1-1, x2, y2-1 )
if direction == "up":
dummy.setX(dummy,-1)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, -90)
self.setCubeTiles( x1-1, y1 )
if direction == "down":
dummy.setX(dummy,1)
self.cube.wrtReparentTo(dummy)
dest_hpr = Vec3(0, 0, 90)
self.setCubeTiles( x1+2, y1 )
else:
print("Invalid move. Waiting ..")
print("Rotating!")
anim = self.animate( LerpHprInterval(dummy, duration, dest_hpr,(0,0,0) ) )
#this sorta.. doesnt belong here.. but i dunno where to put it yet.
x1,y1,x2,y2 = self.getCubeTiles()
# self.level.tintTile(x1,y1) #this is fun to play with if your cube is invisible...
# self.level.tintTile(x2,y2)
self.level.stopAnimatedTile(x1,y1) #stops the tile-animation for the tiles below the block
self.level.stopAnimatedTile(x2,y2)
#cheking what consequences your move had... muhahaa... if you get a 1 .. i've got bad news for you
checkresult = checkMove(self.level.levelNode,self.getCubeTiles(),self.sounds)
if checkresult == 1:
self.falls +=1
self.moves +=1
# Force to the corner when the cuboid falls down
side_force = 1.7
force = Vec3(0, 0, 0)
if direction == "up":
force = Vec3(-side_force, 0, 0)
elif direction == "down":
force = Vec3(side_force, 0, 0)
elif direction == "left":
force = Vec3(0, -side_force, 0)
elif direction == "right":
force = Vec3(0, side_force, 0)
dummy.set_hpr(render, Vec3(0, 0, 0))
del anim
self.setAnimated(True)
final_hpr = dest_hpr * 3.0 + Vec3(random(), random(), random()) * 360.0 * 0.0
anim = LerpFunc(self.animateCube, fromData=0, toData=1, duration=1.3, blendType='noBlend', extraArgs=[dummy.get_pos(render), Vec3(0), final_hpr, dummy, force])
taskMgr.doMethodLater( anim.getDuration(), self.resetCube , "resetTask")
elif checkresult == 2:
#ok.. once reached the goal, move the block down, fading it out. thenload the new level etc.
anim.pop()
anim.append( Func(lambda: self.level.fadeOutLevel() ) )
Sequence(Wait(0.3), Func(lambda *args: self.cube.hide())).start()
taskMgr.doMethodLater( anim.getDuration()+2 , self.levelUp , "lvlup")
taskMgr.doMethodLater( anim.getDuration()+2 , lambda *args: self.cube.show(), "show cube")
taskMgr.doMethodLater( anim.getDuration()+2 , lambda *args: self.shard_node.node().remove_all_children(), "clear shards")
Sequence(Wait(0.2), Func(lambda *args: self.sounds.playSound("finish.wav"))).start()
self.moves = 0
self.falls = 0
cube_min, cube_max = Vec3(-0.5, -0.5, -1), Vec3(0.5, 0.5, 1)
self.shard_node.set_pos(dummy.get_pos(render) + Vec3(-0.5, 0, 0))
shard_size = (cube_max - cube_min) / 5.0
self.shard_node.hide()
Sequence(Wait(0.22), Func(lambda *args: self.shard_node.show())).start()
for i in range(5):
for j in range(5):
for k in range(5):
shard = loader.loadModel("models/CubeShard.bam")
shard.reparent_to(self.shard_node)
shard.set_x(i * shard_size.x + 0.1)
shard.set_y(j * shard_size.y + 0.1)
shard.set_z(k * shard_size.z + 0.2)
shard.set_scale(0.8 + random())
force = Vec3(i-2 - 0.15, j-2 - 0.15, k-2 + 2.6)
force.normalize()
force *= 12.0 * (1 + random() * 0.5)
d_hpr = Vec3(random(), random(), random()) * 360.0 * (3.0 + random())
shard_anim = Sequence(
Wait(0.22),
LerpFunc(self.animateShard, fromData=0, toData=2, duration=2.0, blendType='noBlend', extraArgs=[shard.get_pos(), d_hpr, shard, force]),
LerpHprInterval(shard, 1.0 + random(), d_hpr * 1.6, d_hpr, blendType='noBlend'),
)
shard_anim.start()
elif checkresult == 0:
#how lame... just a ..move..
print("playing sound")
self.moves += 1
self.sounds.playSound("stonerotate.wav")
print("moves:",self.moves ," falls:",self.falls)
#last but not least.. we start the animation .. did you know that the pc knows you'r failing before you actually do? .. scary..
anim.start()
def animateShard(self, t, initial_pos, dest_hpr, shard, force):
z_force = -(t**2) * 9.81 * 1.7
regular_force = force * t
dest_pos = initial_pos + regular_force + Vec3(0, 0, z_force)
shard.set_pos(dest_pos)
shard.set_hpr(dest_hpr * t)
def animateCube(self, t, initial_pos, initial_hpr, dest_hpr, cube, force):
z_force = -(t**2) * 9.81 * 1.7
regular_force = force * t
dest_pos = initial_pos + regular_force + Vec3(0, 0, z_force)
cube.set_pos(render, dest_pos)
cube.set_hpr(render, initial_hpr * (1 - t) + dest_hpr * t)
# cube.set_hpr(render, initial_hpr)