3D camera position restriction issue (total beginner)

Hi everybody.
Im new here, new to coding, new to 3D.
I worked with GDevelop in 2D before, but found myself a little to limited, and ended up here.
I am trying to make a Submarine game, and struggle with, well, everything.

My first question here(for sure,not my last) is about my camera:
I try to get a smooth look when the player is turning, by positioning the camera in alternating X & Z positions, depending on direction, to have the movement emphathised (aka not just the stationary look at the back).
I somehow have it half working, but i fail to set the max. X/Z, the cam can move.
I tryed if self.camera.getX() > self.player.getX()+2 / if self.camera.getX() < self.player.getX()-2 and samefor Z, but somehow that locks my cam in only one direction, while the other is unlimited.

The code, i have so far:

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        self.lens = PerspectiveLens()
        self.lens.setFov(60)
        self.lens.setNear(0.01)
        self.lens.setFar(1000.0)
        self.cam.node().setLens(self.lens)

        self.player = Actor('models/player')
        self.player.reparentTo(render)
        self.player.setScale(.2)

        self.camera.reparentTo(self.player)
        self.camera.setPos( 0, -10, 0)

        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(self.player)
        self.floater.setPos( 0, 5, 0)

        self.disableMouse()       
        props = WindowProperties()
        props.setCursorHidden(True)
        self.win.requestProperties(props)

        self.focus = LVector3(55, -55, 20)
        self.heading = 180
        self.pitch = 0
        self.mousex = 0
        self.mousey = 0
        self.last = 0
        self.mousebtn = [0, 0, 0]

        taskMgr.add(self.controlCamera, "camera-task")
        self.accept("escape", sys.exit, [0])
        self.accept("mouse1", self.setMouseBtn, [0, 1])
        self.accept("mouse1-up", self.setMouseBtn, [0, 0])
        self.accept("mouse2", self.setMouseBtn, [1, 1])
        self.accept("mouse2-up", self.setMouseBtn, [1, 0])
        self.accept("mouse3", self.setMouseBtn, [2, 1])
        self.accept("mouse3-up", self.setMouseBtn, [2, 0])

    def setMouseBtn(self, btn, value):
        self.mousebtn[btn] = value

    def controlCamera(self, task):

        # figure out how much the mouse has moved (in pixels)
        angleDegrees = task.time * 6.0
        delta = globalClock.getDt()
        md = self.win.getPointer(0)
        x = md.getX()
        y = md.getY()
        if self.win.movePointer(0, 100, 100):
            self.heading = self.heading - (x - 100) * 0.2
            self.pitch = self.pitch - (y - 100) * 0.2
        self.camera.lookAt (self.floater)
        self.player.setHpr(self.heading, self.pitch, 0)

        dir = self.player.getMat().getRow3(1)
        elapsed = task.time - self.last
        if self.last == 0:
            elapsed = 0
        if self.mousebtn[0]:
            self.focus = self.focus + dir * elapsed * 30
        if self.mousebtn[1] or self.mousebtn[2]:
            self.focus = self.focus - dir * elapsed * 30
        self.player.setPos(self.focus - (dir * 5))
        dir = self.player.getMat().getRow3(1)
        elapsed = task.time - self.last
        if self.last == 0:
            elapsed = 0
        if self.mousebtn[0]:
            self.focus = self.focus + dir * elapsed * 30
        if self.mousebtn[1] or self.mousebtn[2]:
            self.focus = self.focus - dir * elapsed * 30
        self.player.setPos(self.focus - (dir * 5))
        self.focus = self.player.getPos() + (dir * 5)



"""troublemaker: if i do not include the if not statement, it works pretty well, 
but the cam can run away after too many turns. but if i use this statement, 
the cam gets locked in one direction, while the other still runs away!"""

        if not self.camera.getX() < self.player.getX()-2:
            if x < 100:
                self.camera.setX(self.camera.getX()-5*delta)

        if not self.camera.getX() > self.player.getX()+2:
            if x > 100:
                self.camera.setX(self.camera.getX()+5*delta)
 
       if y < 100:
            self.camera.setZ(self.camera.getZ()+5*delta)
        if y > 100:
            self.camera.setZ(self.camera.getZ()-5*delta)

        self.last = task.time
        return Task.cont

game = Game()
game.run()

i have to admit, that i still dont fully understand how the postioning here works for the player, i just copyed the cameracontrol from one of the examples.
I tryed
if self.camera.getX() > self.player.getX()-2:
if x < 100:
self.camera.setX(self.camera.getX()-5*delta)
wich, of cause has the same result, as the if not statement
what am i missing?

Can you trying describing what you’re trying to do in more detail?

It’s a little unclear to me still.

when the player is turning (mouse moves out of center = changing x & y / heading & pitch)
i want the camera move in that direction.
player turns right, cam moves right
player turns left, cam moves left
player turn up, cam moves up
player turns down, cam moves down
so far, so good, i got that working. (if i left out the “if not” statement, as shown in the y lines)
But i want the cam move only to a certain distrance from the player, restricting it to a max/min position, relative to the player.
i do not get that part working.

in GD i used to put a lot of numbers on screen,when i debug stuff, but i fail to put them on screen in panda.
I would like to put the positions of the cam and the player as text on screen, to figure out whats going on.
how do i display postitions and variables (numbers) as stext on screen?
from direct.gui.OnscreenText import OnscreenText + return OnscreenText did not work

Have you tried printing the values and looking at what’s going on from the terminal?

when i put
print: self.camera.getX()
at the end of my updatetask, nothing is showing up in the terminal.
I am totally new to coding, started a couple of days ago. why is it not printed?

Okay, can you answer a few questions:

  • How are you running the program (is it from the terminal)?
  • What version of python are you using (run python --version from the terminal)?

3.7.6
i run it via the execute button from my IDE Geany
just tryed to run it fromconsole, still no print shows up

Can you

  1. Copy and paste all of your code here
  2. Change the syntax to print(self.camera.getX())
  3. Copy and paste the output that you are getting from the terminal here
1 Like

Thanks, now it prints!

the full main.py

#!/usr/bin/env python

"""
Author: Josh Enes
Last Updated: 2015-03-13

This is a demo of Panda's occluder-culling system. It demonstrates loading
occluder from an EGG file and adding them to a CullTraverser.
"""

# Load PRC data
import sys
import os
from panda3d.core import loadPrcFileData, Vec4, Vec3
#loadPrcFileData("", "want-directtools #t")
#loadPrcFileData("", "want-tk #t")
loadPrcFileData('', 'window-title Occluder Demo')
loadPrcFileData('', 'sync-video false')
loadPrcFileData('', 'show-frame-rate-meter true')
loadPrcFileData('', 'texture-minfilter linear-mipmap-linear')
loadPrcFileData('', 'fake-view-frustum-cull true') # show culled nodes in red

# Import needed modules
import random
from panda3d.core import WindowProperties
from panda3d.core import PandaNode, NodePath, Camera, TextNode, LPoint3, LVector3
from direct.gui.DirectLabel import DirectLabel
from direct.showbase.ShowBase import ShowBase
from direct.task.Task import Task
from direct.showbase import Audio3DManager
#audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], camera)
#ecample:
#mySound = audio3d.loadSfx('blue.wav')
#audio3d.attachSoundToObject(mySound, teapot)
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import PerspectiveLens, TextNode, \
TexGenAttrib, TextureStage, TransparencyAttrib, LPoint3, Texture
from direct.actor.Actor import Actor
import math
from math import *
cos, sin, pi

def add_instructions(pos, msg):
    """Function to put instructions on the screen."""
    return OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1),
                        parent=base.a2dTopLeft, align=TextNode.ALeft,
                        pos=(0.08, -pos - 0.04), scale=.05)

def add_title(text):
    """Function to put title on the screen."""
    return OnscreenText(text=text, style=1, pos=(-0.1, 0.09), scale=.08,
                        parent=base.a2dBottomRight, align=TextNode.ARight,
                        fg=(1, 1, 1, 1), shadow=(0, 0, 0, 1))

def pi ():
    pi = math.pi
    return



class Game(ShowBase):
    """Sets up the game, camera, controls, and loads models."""
    def __init__(self):
        ShowBase.__init__(self)
        self.xray_mode = False
        self.show_model_bounds = False


        # Display instructions
        add_title("Panda3D Tutorial: Occluder Culling")
        add_instructions(0.36, "Arrow Keys: Look Around")
       # Setup camera
        self.lens = PerspectiveLens()
        self.lens.setFov(60)
        self.lens.setNear(0.01)
        self.lens.setFar(1000.0)
        self.cam.node().setLens(self.lens)

        # Load level geometry
        self.level_model = self.loader.loadModel('models/level')
        self.level_model.reparentTo(self.render)
        self.level_model.setTexGen(TextureStage.getDefault(),
                                   TexGenAttrib.MWorldPosition)
        self.level_model.setTexProjector(TextureStage.getDefault(),
                                         self.render, self.level_model)
        self.level_model.setTexScale(TextureStage.getDefault(), 4)
        tex = self.loader.load3DTexture('models/tex_#.png')
        self.level_model.setTexture(tex)

        # Create the main character
        self.player = Actor('models/player')
        self.player.reparentTo(render)
        self.player.setScale(.2)
        self.camera.reparentTo(self.player)
        self.camera.setPos( 0, -10, 0)

        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(self.player)
        self.floater.setPos( 0, -10, 0)
        self.floater2 = NodePath(PandaNode("floater2"))
        self.floater2.reparentTo(self.player)
        self.floater2.setPos( 0, 5, 0)

        occluder_model = self.loader.loadModel('models/occluders')
        occluder_nodepaths = occluder_model.findAllMatches('**/+OccluderNode')
        for occluder_nodepath in occluder_nodepaths:
            self.render.setOccluder(occluder_nodepath)
            occluder_nodepath.node().setDoubleSided(True)

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

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

#        Start the camera control task:
        taskMgr.add(self.controlCamera, "camera-task")
        self.accept("escape", sys.exit, [0])
        self.accept("mouse1", self.setMouseBtn, [0, 1])
        self.accept("mouse1-up", self.setMouseBtn, [0, 0])
        self.accept("mouse2", self.setMouseBtn, [1, 1])
        self.accept("mouse2-up", self.setMouseBtn, [1, 0])
        self.accept("mouse3", self.setMouseBtn, [2, 1])
        self.accept("mouse3-up", self.setMouseBtn, [2, 0])
        self.accept("arrow_left", self.rotateCam, [-1])
        self.accept("arrow_right", self.rotateCam, [1])

    def setMouseBtn(self, btn, value):
        self.mousebtn[btn] = value

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

    def controlCamera(self, task):

        # figure out how much the mouse has moved (in pixels)
        self.camera.lookAt (self.floater2)
        angleDegrees = task.time * 6.0
        delta = globalClock.getDt()

        md = self.win.getPointer(0)
        x = md.getX()
        y = md.getY()
        if self.win.movePointer(0, 100, 100):
            self.heading = self.heading - (x - 100) * 0.2
            self.pitch = self.pitch - (y - 100) * 0.2

        self.player.setHpr(self.heading, self.pitch, 0)

        dir = self.player.getMat().getRow3(1)
        elapsed = task.time - self.last
        if self.last == 0:
            elapsed = 0
        if self.mousebtn[0]:
            self.focus = self.focus + dir * elapsed * 30
        if self.mousebtn[1] or self.mousebtn[2]:
            self.focus = self.focus - dir * elapsed * 30
        self.player.setPos(self.focus - (dir * 5))

        self.focus = self.player.getPos() + (dir * 5)
        
        if x < 100:
            self.camera.setX(self.camera.getX()-5*delta)
        if x > 100:
            self.camera.setX(self.camera.getX()+5*delta)
        if y < 100:
            self.camera.setZ(self.camera.getZ()+5*delta)
        if y > 100:
            self.camera.setZ(self.camera.getZ()-5*delta)

        print(self.camera.getX())
        self.last = task.time
        return Task.cont

 
game = Game()
game.run()

now, that i can get the numbers, i see, why its not working. the player positions are world related, the cam player related.
Problem solved, just compared my cam pos with my floater, instead of the player :smiley:
Thanks!