samples: various improvements to Roaming Ralph sample:

- add a skybox color (required making boundary around environment transparent)
- use CollisionHandlerPusher and spheres to detect collisions with obstacles
- add ability to walk backwards
- increase minimum distance between follow camera and terrain, to avoid clipping through
- make Ralph run slightly less ridiculously fast

Closes #565

Co-authored-by: rdb <git@rdb.name>
This commit is contained in:
Mitchell Stokes 2019-04-02 22:17:48 +02:00 committed by rdb
parent fff422fab7
commit 017b4b5835
2 changed files with 70 additions and 37 deletions

View File

@ -12,6 +12,7 @@
from direct.showbase.ShowBase import ShowBase from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionNode from panda3d.core import CollisionTraverser, CollisionNode
from panda3d.core import CollisionHandlerQueue, CollisionRay from panda3d.core import CollisionHandlerQueue, CollisionRay
from panda3d.core import CollisionHandlerPusher, CollisionSphere
from panda3d.core import Filename, AmbientLight, DirectionalLight from panda3d.core import Filename, AmbientLight, DirectionalLight
from panda3d.core import PandaNode, NodePath, Camera, TextNode from panda3d.core import PandaNode, NodePath, Camera, TextNode
from panda3d.core import CollideMask from panda3d.core import CollideMask
@ -40,12 +41,15 @@ class RoamingRalphDemo(ShowBase):
# Set up the window, camera, etc. # Set up the window, camera, etc.
ShowBase.__init__(self) ShowBase.__init__(self)
# Set the background color to black
self.win.setClearColor((0, 0, 0, 1))
# This is used to store which keys are currently pressed. # This is used to store which keys are currently pressed.
self.keyMap = { self.keyMap = {
"left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0} "left": 0,
"right": 0,
"forward": 0,
"backward": 0,
"cam-left": 0,
"cam-right": 0,
}
# Post the instructions # Post the instructions
self.title = addTitle( self.title = addTitle(
@ -54,8 +58,9 @@ class RoamingRalphDemo(ShowBase):
self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left")
self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right")
self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward")
self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") self.inst5 = addInstructions(0.30, "[Down Arrow]: Walk Ralph Backward")
self.inst7 = addInstructions(0.36, "[S]: Rotate Camera Right") self.inst6 = addInstructions(0.36, "[A]: Rotate Camera Left")
self.inst7 = addInstructions(0.42, "[S]: Rotate Camera Right")
# Set up the environment # Set up the environment
# #
@ -72,6 +77,9 @@ class RoamingRalphDemo(ShowBase):
self.environ = loader.loadModel("models/world") self.environ = loader.loadModel("models/world")
self.environ.reparentTo(render) self.environ.reparentTo(render)
# We do not have a skybox, so we will just use a sky blue background color
self.setBackgroundColor(0.53, 0.80, 0.92, 1)
# Create the main character, Ralph # Create the main character, Ralph
ralphStartPos = self.environ.find("**/start_point").getPos() ralphStartPos = self.environ.find("**/start_point").getPos()
@ -80,7 +88,7 @@ class RoamingRalphDemo(ShowBase):
"walk": "models/ralph-walk"}) "walk": "models/ralph-walk"})
self.ralph.reparentTo(render) self.ralph.reparentTo(render)
self.ralph.setScale(.2) self.ralph.setScale(.2)
self.ralph.setPos(ralphStartPos + (0, 0, 0.5)) self.ralph.setPos(ralphStartPos + (0, 0, 1.5))
# Create a floater object, which floats 2 units above ralph. We # Create a floater object, which floats 2 units above ralph. We
# use this as a target for the camera to look at. # use this as a target for the camera to look at.
@ -95,31 +103,51 @@ class RoamingRalphDemo(ShowBase):
self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_left", self.setKey, ["left", True])
self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_right", self.setKey, ["right", True])
self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_up", self.setKey, ["forward", True])
self.accept("arrow_down", self.setKey, ["backward", True])
self.accept("a", self.setKey, ["cam-left", True]) self.accept("a", self.setKey, ["cam-left", True])
self.accept("s", self.setKey, ["cam-right", True]) self.accept("s", self.setKey, ["cam-right", True])
self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_left-up", self.setKey, ["left", False])
self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_right-up", self.setKey, ["right", False])
self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_up-up", self.setKey, ["forward", False])
self.accept("arrow_down-up", self.setKey, ["backward", False])
self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("a-up", self.setKey, ["cam-left", False])
self.accept("s-up", self.setKey, ["cam-right", False]) self.accept("s-up", self.setKey, ["cam-right", False])
taskMgr.add(self.move, "moveTask") taskMgr.add(self.move, "moveTask")
# Game state variables
self.isMoving = False
# Set up the camera # Set up the camera
self.disableMouse() self.disableMouse()
self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2) self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 10, 2)
self.cTrav = CollisionTraverser()
# Use a CollisionHandlerPusher to handle collisions between Ralph and
# the environment. Ralph is added as a "from" object which will be
# "pushed" out of the environment if he walks into obstacles.
#
# Ralph is composed of two spheres, one around the torso and one
# around the head. They are slightly oversized since we want Ralph to
# keep some distance from obstacles.
self.ralphCol = CollisionNode('ralph')
self.ralphCol.addSolid(CollisionSphere(center=(0, 0, 2), radius=1.5))
self.ralphCol.addSolid(CollisionSphere(center=(0, -0.25, 4), radius=1.5))
self.ralphCol.setFromCollideMask(CollideMask.bit(0))
self.ralphCol.setIntoCollideMask(CollideMask.allOff())
self.ralphColNp = self.ralph.attachNewNode(self.ralphCol)
self.ralphPusher = CollisionHandlerPusher()
self.ralphPusher.horizontal = True
# Note that we need to add ralph both to the pusher and to the
# traverser; the pusher needs to know which node to push back when a
# collision occurs!
self.ralphPusher.addCollider(self.ralphColNp, self.ralph)
self.cTrav.addCollider(self.ralphColNp, self.ralphPusher)
# We will detect the height of the terrain by creating a collision # We will detect the height of the terrain by creating a collision
# ray and casting it downward toward the terrain. One ray will # ray and casting it downward toward the terrain. One ray will
# start above ralph's head, and the other will start above the camera. # start above ralph's head, and the other will start above the camera.
# A ray may hit the terrain, or it may hit a rock or a tree. If it # A ray may hit the terrain, or it may hit a rock or a tree. If it
# hits the terrain, we can detect the height. If it hits anything # hits the terrain, we can detect the height.
# else, we rule that the move is illegal.
self.cTrav = CollisionTraverser()
self.ralphGroundRay = CollisionRay() self.ralphGroundRay = CollisionRay()
self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setOrigin(0, 0, 9)
self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundRay.setDirection(0, 0, -1)
@ -143,7 +171,7 @@ class RoamingRalphDemo(ShowBase):
self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler)
# Uncomment this line to see the collision rays # Uncomment this line to see the collision rays
#self.ralphGroundColNp.show() #self.ralphColNp.show()
#self.camGroundColNp.show() #self.camGroundColNp.show()
# Uncomment this line to show a visual representation of the # Uncomment this line to show a visual representation of the
@ -181,11 +209,6 @@ class RoamingRalphDemo(ShowBase):
if self.keyMap["cam-right"]: if self.keyMap["cam-right"]:
self.camera.setX(self.camera, +20 * dt) self.camera.setX(self.camera, +20 * dt)
# save ralph's initial position so that we can restore it,
# in case he falls off the map or runs into something.
startpos = self.ralph.getPos()
# If a move-key is pressed, move ralph in the specified direction. # If a move-key is pressed, move ralph in the specified direction.
if self.keyMap["left"]: if self.keyMap["left"]:
@ -193,17 +216,28 @@ class RoamingRalphDemo(ShowBase):
if self.keyMap["right"]: if self.keyMap["right"]:
self.ralph.setH(self.ralph.getH() - 300 * dt) self.ralph.setH(self.ralph.getH() - 300 * dt)
if self.keyMap["forward"]: if self.keyMap["forward"]:
self.ralph.setY(self.ralph, -25 * dt) self.ralph.setY(self.ralph, -20 * dt)
if self.keyMap["backward"]:
self.ralph.setY(self.ralph, +10 * dt)
# If ralph is moving, loop the run animation. # If ralph is moving, loop the run animation.
# If he is standing still, stop the animation. # If he is standing still, stop the animation.
currentAnim = self.ralph.getCurrentAnim()
if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"]: if self.keyMap["forward"]:
if self.isMoving is False: if currentAnim != "run":
self.ralph.loop("run") self.ralph.loop("run")
self.isMoving = True elif self.keyMap["backward"]:
# Play the walk animation backwards.
if currentAnim != "walk":
self.ralph.loop("walk")
self.ralph.setPlayRate(-1.0, "walk")
elif self.keyMap["left"] or self.keyMap["right"]:
if currentAnim != "walk":
self.ralph.loop("walk")
self.ralph.setPlayRate(1.0, "walk")
else: else:
if self.isMoving: if currentAnim is not None:
self.ralph.stop() self.ralph.stop()
self.ralph.pose("walk", 5) self.ralph.pose("walk", 5)
self.isMoving = False self.isMoving = False
@ -228,25 +262,24 @@ class RoamingRalphDemo(ShowBase):
#self.cTrav.traverse(render) #self.cTrav.traverse(render)
# Adjust ralph's Z coordinate. If ralph's ray hit terrain, # Adjust ralph's Z coordinate. If ralph's ray hit terrain,
# update his Z. If it hit anything else, or didn't hit anything, put # update his Z
# him back where he was last frame.
entries = list(self.ralphGroundHandler.getEntries()) entries = list(self.ralphGroundHandler.entries)
entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ())
if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": for entry in entries:
self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) if entry.getIntoNode().getName() == "terrain":
else: self.ralph.setZ(entry.getSurfacePoint(render).getZ())
self.ralph.setPos(startpos)
# Keep the camera at one foot above the terrain, # Keep the camera at one unit above the terrain,
# or two feet above ralph, whichever is greater. # or two units above ralph, whichever is greater.
entries = list(self.camGroundHandler.getEntries()) entries = list(self.camGroundHandler.entries)
entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ())
if len(entries) > 0 and entries[0].getIntoNode().getName() == "terrain": for entry in entries:
self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.0) if entry.getIntoNode().getName() == "terrain":
self.camera.setZ(entry.getSurfacePoint(render).getZ() + 1.5)
if self.camera.getZ() < self.ralph.getZ() + 2.0: if self.camera.getZ() < self.ralph.getZ() + 2.0:
self.camera.setZ(self.ralph.getZ() + 2.0) self.camera.setZ(self.ralph.getZ() + 2.0)