Problem: flying a drone using the sample Roaming-ralph

Hi there !!!
I have a little problem using the sample Roaming-Ralph for my apptest cuz I want to make my drone flying on the Z axis and I think that’s because I apply a collision on this Z axis but if I handle collisions on the Y axis for instance then my drone got through the ground. I am very new on Panda programming…

here is my program below :


from panda3d.core import loadPrcFileData
# Configure the parallax mapping settings (these are just the defaults)
loadPrcFileData("", "parallax-mapping-samples 3")
loadPrcFileData("", "parallax-mapping-scale 0.1")

import direct.directbase.DirectStart
from panda3d.core import WindowProperties
from panda3d.core import Filename,Shader
from panda3d.core import AmbientLight,PointLight
from panda3d.core import TextNode
from panda3d.core import Point3,Vec3,Vec4
from direct.task.Task import Task
from direct.actor.Actor import Actor
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.filter.CommonFilters import *

from panda3d.core import CollisionTraverser,CollisionNode
from panda3d.core import CollisionHandlerQueue,CollisionRay
from panda3d.core import CollisionTube,CollisionSegment
from panda3d.core import AmbientLight,DirectionalLight
from panda3d.core import PandaNode,NodePath,Camera,TextNode
from panda3d.core import BitMask32
from panda3d.core import LightRampAttrib
import random, math

import sys,os

SPEED = 0.5

# Function to put instructions on the screen.
def addInstructions(pos, msg):
    return OnscreenText(text=msg, style=1, fg=(1,1,1,1),
                        pos=(-1.3, pos), align=TextNode.ALeft, scale = .05)

# Function to put title on the screen.
def addTitle(text):
    return OnscreenText(text=text, style=1, fg=(1,1,1,1),
                        pos=(1.3,-0.95), align=TextNode.ARight, scale = .07)

def mousePos(self, x, y, z):
    print("X:"+str(x)+" Y:"+str(y)+ " Z:"+str(z))
        
class Drone(DirectObject):

    def __init__(self):

        # Check video card capabilities.

        if (base.win.getGsg().getSupportsBasicShaders() == 0):
            addTitle("Drone: Video driver reports that shaders are not supported.")
            return

        self.controlMap = {"left":0, "right":0, "forward":0, "backward":0,
            "zoom-in":0, "zoom-out":0, "wheel-in":0, "wheel-out":0, "mouse1":0, "mouse3":0, "leftaround":0, "rightaround":0}
        
        # Post the instructions
        self.title = addTitle("Panda3D: AppTest - Drone")
        self.inst1 = addInstructions(0.95, "Press ESC to exit")
        self.inst2 = addInstructions(0.90, "Move mouse to rotate camera")
        self.inst3 = addInstructions(0.85, "Left mouse button: Move forwards")
        self.inst4 = addInstructions(0.80, "Right mouse button: Move backwards")
        self.inst5 = addInstructions(0.75, "Enter: Turn bump maps Off")
        self.inst6 = addInstructions(0.70, "Z Q S D keys move the drone forward, left, back, and right, respectively.")
        self.inst7 = addInstructions(0.65, "Zoom in and out using the mouse wheel, or page up and page down keys.")

        # Load the 'abstract room' model.  This is a model of an
        # empty room containing a pillar, a pyramid, and a bunch
        # of exaggeratedly bumpy textures.

        self.environ = loader.loadModel("models/world")
        self.environ.reparentTo(render)
        self.environ.setPos(0,0,0)

        self.walls = self.environ.find("**/wall")

        droneStartPos = self.environ.find("**/start_point").getPos()
        self.drone = loader.loadModel("models/sad_drone")
        self.drone.reparentTo(render)
        self.drone.setPos(droneStartPos)
        
        mytextdrone = loader.loadTexture("models/sad_drone_diffuse.png")
        self.drone.setTexture(mytextdrone)

# Make the mouse invisible, turn off normal mouse controls
        base.disableMouse()
        props = WindowProperties()
        props.setCursorHidden(True)
        base.win.requestProperties(props)

        # Set the current viewing target
        self.focus = Vec3(55,50,10)
        self.heading = 180
        self.pitch = 0
        self.mousex = 0
        self.mousey = 0
        self.last = 0
        self.mousebtn = [0,0,0]


        self.accept("escape", sys.exit, [0])
        self.accept("z", self.setControl, ["forward",1])
        self.accept("q", self.setControl, ["left",1])
        self.accept("s", self.setControl, ["backward",1])
        self.accept("d", self.setControl, ["right",1])
        self.accept("z-up", self.setControl, ["forward",0])
        self.accept("q-up", self.setControl, ["left",0])
        self.accept("s-up", self.setControl, ["backward",0])
        self.accept("d-up", self.setControl, ["right",0])
        self.accept("mouse1", self.setControl, ["mouse1", 1])
        self.accept("mouse1-up", self.setControl, ["mouse1", 0])
        self.accept("mouse3", self.setControl, ["mouse3", 1])
        self.accept("mouse3-up", self.setControl, ["mouse3", 0])

        self.accept("a", self.setControl, ["leftaround",1])
        self.accept("e", self.setControl, ["rightaround",1])
        self.accept("a-up", self.setControl, ["leftaround",0])
        self.accept("e-up", self.setControl, ["rightaround",0])
        #self.accept("mouse3", self.setMouseBtn, [2, 1])
        #self.accept("mouse3-up", self.setMouseBtn, [2, 0])
        self.accept("enter", self.toggleShader)
        self.accept("j", self.rotateLight, [-1])
        self.accept("k", self.rotateLight, [1])
        self.accept("arrow_left", self.rotateCam, [-1])
        self.accept("arrow_right", self.rotateCam, [1])
        self.accept("wheel_up", self.setControl, ["wheel-in", 1])
        self.accept("wheel_down", self.setControl, ["wheel-out", 1])
        self.accept("page_up", self.setControl, ["zoom-in", 1])
        self.accept("page_up-up", self.setControl, ["zoom-in", 0])
        self.accept("page_down", self.setControl, ["zoom-out", 1])
        self.accept("page_down-up", self.setControl, ["zoom-out", 0])
        
        # Start the camera control task:
        
        #taskMgr.add(self.controlCamera, "camera-task")

        taskMgr.add(self.move,"moveTask")

        # Game state variables
        self.isMoving = False

        # Set up the camera
        # Adding the camera to Drone is a simple way to keep the camera locked
        # in behind Drone regardless of ralph's movement.
        base.camera.reparentTo(self.drone)
        # We don't actually want to point the camera at Drone's feet.
        # This value will serve as a vertical offset so we can look over Drone
        self.cameraTargetHeight = 6.0
        # How far should the camera be from Drone
        self.cameraDistance = 30
        # Initialize the pitch of the camera
        self.cameraPitch = 10

        # We will detect the height of the terrain by creating a collision
        # ray and casting it downward toward the terrain.  One ray will
        # start above ralph's head.
        # 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
        # else, we rule that the move is illegal.

        self.cTrav = CollisionTraverser()

        self.droneGroundRay = CollisionRay()
        self.droneGroundRay.setOrigin(0,0,1000)
        self.droneGroundRay.setDirection(0,0,-1)
        self.droneGroundCol = CollisionNode('droneRay')
        self.droneGroundCol.addSolid(self.droneGroundRay)
        self.droneGroundCol.setFromCollideMask(BitMask32.bit(0))
        self.droneGroundCol.setIntoCollideMask(BitMask32.allOff())
        self.droneGroundColNp = self.drone.attachNewNode(self.droneGroundCol)
        self.droneGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.droneGroundColNp, self.droneGroundHandler)
        
        # We will detect anything obstructing the camera's view of the player

        self.cameraRay = CollisionSegment((0,0,self.cameraTargetHeight),(0,5,5))
        self.cameraCol = CollisionNode('cameraRay')
        self.cameraCol.addSolid(self.cameraRay)
        self.cameraCol.setFromCollideMask(BitMask32.bit(0))
        self.cameraCol.setIntoCollideMask(BitMask32.allOff())
        self.cameraColNp = self.drone.attachNewNode(self.cameraCol)
        self.cameraColHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.cameraColNp, self.cameraColHandler)

        # Create some lighting
        ambientLight = AmbientLight("ambientLight")
        ambientLight.setColor(Vec4(.3, .3, .3, 1))
        directionalLight = DirectionalLight("directionalLight")
        directionalLight.setDirection(Vec3(-5, -5, -5))
        directionalLight.setColor(Vec4(1, 1, 1, 1))
        directionalLight.setSpecularColor(Vec4(1, 1, 1, 1))
        render.setLight(render.attachNewNode(ambientLight))
        render.setLight(render.attachNewNode(directionalLight))

        #Records the state of the arrow keys
    def setControl(self, key, value):
        self.controlMap[key] = value
        
    def setMouseBtn(self, btn, value):
        self.mousebtn[btn] = value

    def rotateLight(self, offset):
        self.lightpivot.setH(self.lightpivot.getH()+offset*20)

    def rotateCam(self, offset):
        self.heading = self.heading - offset*10

    def toggleShader(self):
        self.inst5.destroy()
        if (self.shaderenable):
            self.inst5 = addInstructions(0.75, "Enter: Turn bump maps On")
            self.shaderenable = 0
            self.room.setShaderOff()
        else:
            self.inst5 = addInstructions(0.75, "Enter: Turn bump maps Off")
            self.shaderenable = 1
            self.room.setShaderAuto()

    # Accepts arrow keys to move either the player or the menu cursor,
    # Also deals with grid checking and collision detection
    def move(self, task):

        # save teh drone's initial position so that we can restore it,
        # in case it falls off the map or runs into something.

        startpos = self.drone.getPos()
         # If a move-key is pressed, move the drone in the specified direction.
        if (self.controlMap["forward"]!=0):
            self.drone.setY(self.drone, -25 * globalClock.getDt())
        if (self.controlMap["backward"]!=0):
            self.drone.setY(self.drone, 25 * globalClock.getDt())
        if (self.controlMap["left"]!=0):
            self.drone.setX(self.drone, 25 * globalClock.getDt())
        if (self.controlMap["right"]!=0):
            self.drone.setX(self.drone, -25 * globalClock.getDt())
        if (self.controlMap["mouse1"]!=0):
            self.drone.setZ(self.drone, -25 * globalClock.getDt())
        if (self.controlMap["mouse3"]!=0):
            self.drone.setZ(self.drone, 25 * globalClock.getDt())
        if (self.controlMap["leftaround"]!=0):
            self.drone.setH(self.drone.getH() + 300 * globalClock.getDt())
        if (self.controlMap["rightaround"]!=0):
            self.drone.setH(self.drone.getH() - 300 * globalClock.getDt())


        # If a zoom button is pressed, zoom in or out
        if (self.controlMap["wheel-in"]!=0):
            self.cameraDistance -= 0.1 * self.cameraDistance;
            if (self.cameraDistance < 5):
                self.cameraDistance = 5
            self.controlMap["wheel-in"] = 0
        elif (self.controlMap["wheel-out"]!=0):
            self.cameraDistance += 0.1 * self.cameraDistance;
            if (self.cameraDistance > 250):
                self.cameraDistance = 250
            self.controlMap["wheel-out"] = 0
        if (self.controlMap["zoom-in"]!=0):
            self.cameraDistance -= globalClock.getDt() * self.cameraDistance;
            if (self.cameraDistance < 5):
                self.cameraDistance = 5
        elif (self.controlMap["zoom-out"]!=0):
            self.cameraDistance += globalClock.getDt() * self.cameraDistance;
            if (self.cameraDistance > 250):
                self.cameraDistance = 250

        # Use mouse input to turn both Drone and the Camera
        if base.mouseWatcherNode.hasMouse():
            # get changes in mouse position
            md = base.win.getPointer(0)
            x = md.getX()
            y = md.getY()
            deltaX = md.getX() - 200
            deltaY = md.getY() - 200
            # reset mouse cursor position
            base.win.movePointer(0, 200, 200)
            # alter drone's yaw by an amount proportionate to deltaX
            self.drone.setH(self.drone.getH() - 0.3* deltaX)
            # find the new camera pitch and clamp it to a reasonable range
            self.cameraPitch = self.cameraPitch + 0.1 * deltaY
            if (self.cameraPitch < -60): self.cameraPitch = -60
            if (self.cameraPitch >  80): self.cameraPitch =  80
            base.camera.setHpr(0,self.cameraPitch,0)
            # set the camera at around ralph's middle
            # We should pivot around here instead of the view target which is noticebly higher
            base.camera.setPos(0,0,self.cameraTargetHeight/2)
            # back the camera out to its proper distance
            base.camera.setY(base.camera,self.cameraDistance)

        # point the camera at the view target
        viewTarget = Point3(0,0,self.cameraTargetHeight)
        base.camera.lookAt(viewTarget)
        # reposition the end of the  camera's obstruction ray trace
        self.cameraRay.setPointB(base.camera.getPos())

        # If the drone is moving, loop the run animation.
        # If he is standing still, stop the animation.

        if (self.controlMap["forward"]!=0) or (self.controlMap["left"]!=0) or (self.controlMap["right"]!=0) or (self.controlMap["backward"]!=0):
            if self.isMoving is False:
                #self.drone.loop("run")
                self.isMoving = True
        else:
            if self.isMoving:
                #self.drone.stop()
                #self.drone.pose("walk",5)
                self.isMoving = False
                
        # Now check for collisions.

        self.cTrav.traverse(render)

        # Adjust drone's Z coordinate.  If drone's ray hit terrain,
        # update his Z. If it hit anything else, or didn't hit anything, put
        # him back where he was last frame.

        entries = []
        for i in range(self.droneGroundHandler.getNumEntries()):
            entry = self.droneGroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ()))
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.drone.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.drone.setPos(startpos)
            
        return task.cont

d = Drone()

run()

If somebody have any idea then plz tell me :wink:

Thanks

(PS: I am from France so plz forgive me for my english :wink: )

Hi and welcome!

I do not yet quite understand the problem you are having, but from your code i understand that you want to allow the drone to fly up (increase z), but it keeps sticking to the ground. If this is indeed the problem then the solution might be to change your (almost) last line of code:

if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.drone.setZ(entries[0].getSurfacePoint(render).getZ())

by:

if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain") and self.drone.getZ()<entries[0].getSurfacePoint(render).getZ():
            self.drone.setZ(entries[0].getSurfacePoint(render).getZ())

This will make te drone only set its z-coordinate to that of the floor when it is lower than the floor, and not when it is flying upwards.

Thank you for responding so quickly !
I changed the line like you suggested but now when there is a little hill and when my drone is on the ground then it will takes the coordinates of the hill and therefore my drone will fly whereas it shouldn’t.
I don’t know if you understand but it is like the drone fly after “climbed” a hill…
I will try by myself to make this better but I am also open for any suggestion :mrgreen:
I will post the result if it works better.
But thanks again, you make my drone flying better than before (even if it didn’t fly :mrgreen: )

Haha, Ok we’re getting there. What you need is a little gravity. The most basic (but sufficient for now) way of adding gravity is by giving the Drone an attribute self.vz (velocity z). Every frame this velocity should be incremented by the gravitational acceleration, self.az (acceleration z), say a value of -10. If the drone hits the ground then the velocity should be set back to 0.
in the init method:

self.az = -10.
self.vz = 0

Try to make those lines from the previous post:

self.vz += self.az
self.drone.setZ(self.drone,self.vz)
if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain") and self.drone.getZ()<entries[0].getSurfacePoint(render).getZ():
            self.vz = 0
            self.drone.setZ(entries[0].getSurfacePoint(render).getZ())

Or instead of self.vz = 0 you could make it self.vz = -0.6*self.vz to make the drone bounce a little.

There are very sophisticated physics engines out there but if you are just starting with Panda that might be a little overwhelming.

With this code you are probably going to get problems when you are trying tofly (move up the z direction), after a while you will fall down anyway. You might want to come up with a solution to that.

Ok, the idea of adding gravity sounds good and I must go on that way, thanks.
I added what you suggested me and the Drone didn’t fly at all. :cry:
Perhaps I missed something but I checked twice a time before running the program :stuck_out_tongue:
I will check this out after learning a little bit about how to set up a system of gravity
Thanks to guide me on that way

If you’re saying the drone got stuck to the ground again, then you should probably lower the gravity acceleration: self.az = -10 to self.az = -0.10
Apparently the value of -10 is so much gravity that your drone cant get off the floor.

Exactly !
I even change the value of

to 0.005 and the drone did exactly what you said. It bounces a little. Now I am checking how to keep it flying when it takes off and to keep it stucking on the ground when I don’t press the take off button (right click).

I added that in my code with self.az = 0.005 in the init method :

        self.vz += self.az
        self.drone.setZ(self.drone, self.vz)
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain") and self.drone.getZ() < entries[0].getSurfacePoint(render).getZ():
            self.vz = 0
            self.az = 0.005
            self.drone.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.az = 0

My drone starts flying without pressing any button and I think we need to set a “mass” to the drone to keep it on the ground. Is it a good idea? I mean is that possible to assign a “mass” to a model ?

Well, in physics you would work with Forces, different forces acting on an object (gravity, objects interacting, bouncing) and together with a mass you would be able to calculate accelerations.

You could definately do thing in your program, which would indeed requires assinging a mass to your model and i think this is also something you would do when you would use extensive physics engines. However, in the code you have thusfar we are skipping the whole mass and forces part, and jump directly to accelerations.

The problem is that you are using a positive self.az, this means accelerating towards the sky. Try using self.az = -0.005 (so negative; accelerating towards the ground, which is probably what you mean whe you say ‘add mass to the model’)

Actually, I don’t know but what is the logical meaning of flying ?
I mean if a drone is flying, it will not go down slowly but it will rather keep at the same height, right ?
I think I started with a wrong logic, that’s why my method was not working.
I’ll still work on it to add Forces as you said.