Help complete 6DoF controller. Issue with motion.

I have a problem with my controller that puts things in motion, but not as desired. It seems the character jumps between the new position and the old one when I hold whatever movement direction key, as if he were shaking. I guess the problem is related to how I’m organizing my file, because somewhere below there is a piece of code that both clamps the maximum speed and decelerates when the speed is different than 0. I guess I should try to clamp the speed and decelerate separately, but how? For both mouse and keyboard input, by the way (which could be tackled by the same restructuring).

def setSpeedValues(self):
	
		# Define static variables to be changed through the control method
		self.mouseXSpeed = 0
		self.mouseYSpeed = 0
		self.forward = 0
		self.sides = 0
		self.vert = 0
		self.roll = 0
		
	def lookControl(self, task):
		
		# Define orientation vector and rotation quaternion
		# to be transformed by mouse input
		self.rotateQuat = Quat()
		
		# Let's define the new orientation through mouse coordinates
		# Get mouse coordinates
		md = base.win.getPointer(0)
		x = md.getX()
		y = md.getY()
		
		# Centre the cursor and register its displacement in delta variables
		if base.win.movePointer(0, base.win.getXSize()/2, base.win.getYSize()/2):
			deltaX = x - base.win.getXSize()/2
			deltaY = y - base.win.getYSize()/2
				
			if (deltaX != 0):
				self.mouseXSpeed = deltaX * TURN_ACCEL * MAX_TURN_SPEED * -1
			if (deltaY != 0):
				self.mouseYSpeed = deltaY * TURN_ACCEL * MAX_TURN_SPEED * -1
		
		# Apply deceleration when the cursor stops moving but still carries some speed
		if (self.mouseXSpeed != 0):
			self.mouseXSpeed = max(min(self.mouseXSpeed, MAX_TURN_SPEED), -MAX_TURN_SPEED)
			lerpSpeed = self.mouseXSpeed + (0 - self.mouseXSpeed) * TURN_DECEL
			self.mouseXSpeed = max(min(lerpSpeed, 0), lerpSpeed)
		if (self.mouseYSpeed != 0):
			self.mouseYSpeed = max(min(self.mouseYSpeed, MAX_TURN_SPEED), -MAX_TURN_SPEED)
			lerpSpeed = self.mouseYSpeed + (0 - self.mouseYSpeed) * TURN_DECEL
			self.mouseYSpeed = max(min(lerpSpeed, 0), lerpSpeed)
				
		# Transform the roll value
		if (self.keyMap["roll-right"] != 0):
			self.roll += ROLL_ACCEL * MAX_ROLL_SPEED
		if (self.keyMap["roll-left"] != 0):
			self.roll -= ROLL_ACCEL * MAX_ROLL_SPEED
			
		# Clamp the roll value to the maximum speed values and
		# interpolate with zero when the key is released
		if (self.roll != 0):
			self.roll = max(min(self.roll, MAX_ROLL_SPEED), -MAX_ROLL_SPEED)
			lerpRoll = self.roll + (0 - self.roll) * ROLL_DECEL
			self.roll = max(min(lerpRoll, 0), lerpRoll)
				
		self.rotateQuat.setHpr(Vec3(self.mouseXSpeed, self.mouseYSpeed, self.roll))
		self.character.setQuat(self.character, self.rotateQuat)
			
		return task.cont
			
	def moveControl(self, task):
	
		# Move the character by incrementing speed values
		# when the keys are pressed
		
		# Transform the forward value
		if (self.keyMap["forward"] != 0):
			self.forward += MOVE_ACCEL * MAX_MOVE_SPEED
		if (self.keyMap["backward"] != 0):
			self.forward -= MOVE_ACCEL * MAX_MOVE_SPEED
		if (self.keyMap["right"] != 0):
			self.sides += MOVE_ACCEL * MAX_MOVE_SPEED
		if (self.keyMap["left"] != 0):
			self.sides -= MOVE_ACCEL * MAX_MOVE_SPEED
		if (self.keyMap["up"] != 0):
			self.vert += MOVE_ACCEL * MAX_MOVE_SPEED
		if (self.keyMap["down"] != 0):
			self.vert -= MOVE_ACCEL * MAX_MOVE_SPEED
			
		# Clamp the forward value to the maximum speed values and
		# interpolate with zero when the key is released
		if (self.forward != 0):
			self.forward = max(min(self.forward, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
			lerpForward = self.forward + (0 - self.forward) * MOVE_DECEL
			self.forward = max(min(lerpForward, 0), lerpForward)
			wantVec = self.character.getQuat(render).getForward()
			self.character.setPos(self.character.getPos() + wantVec * self.forward * globalClock.getDt())
			
		if (self.sides != 0):
			self.sides = max(min(self.sides, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
			lerpSides = self.sides + (0 - self.sides) * MOVE_DECEL
			self.sides = max(min(lerpSides, 0), lerpSides)
			wantVec = self.character.getQuat(render).getRight()
			self.character.setPos(self.character.getPos() + wantVec * self.sides * globalClock.getDt())
			
		if (self.vert != 0):
			self.vert = max(min(self.vert, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
			lerpVert = self.vert + (0 - self.vert) * MOVE_DECEL
			self.vert = max(min(lerpVert, 0), lerpVert)
			wantVec = self.character.getQuat(render).getUp()
			self.character.setPos(self.character.getPos() + wantVec * self.vert * globalClock.getDt())
		
		return task.cont

Ok, I added this line to the Config.prc file:

sync-video #f

And the character doesn’t stutter anymore, but now I have another problem. The character moves and turns too fast. If I multiply the speed by delta time I get what I want, except for rolling. Rolling without delta time is too fast, while it’s too slow with it. And I mean slow to the point of barely moving. Shouldn’t it behave similarly to movement? Here’s the code:

def setSpeedValues(self):
	
		# Define static variables to be changed through the control method
		self.mouseXSpeed = 0
		self.mouseYSpeed = 0
		self.forward = 0
		self.sides = 0
		self.vert = 0
		self.roll = 0
		
	def lookControl(self, task):
		
		# Let's define the new orientation through mouse coordinates
		# Get mouse coordinates
		md = base.win.getPointer(0)
		x = md.getX()
		y = md.getY()
		
		# Centre the cursor and register its displacement in delta variables
		if base.win.movePointer(0, base.win.getXSize()/2, base.win.getYSize()/2):
			deltaX = x - base.win.getXSize()/2
			deltaY = y - base.win.getYSize()/2
				
			if (deltaX != 0):
				self.mouseXSpeed = deltaX * TURN_ACCEL * MAX_TURN_SPEED * -1
			if (deltaY != 0):
				self.mouseYSpeed = deltaY * TURN_ACCEL * MAX_TURN_SPEED * -1
		
		# Clamp the mouse speed values to the maximum speed values
		self.mouseXSpeed = max(min(self.mouseXSpeed, MAX_TURN_SPEED), -MAX_TURN_SPEED)
		self.mouseYSpeed = max(min(self.mouseYSpeed, MAX_TURN_SPEED), -MAX_TURN_SPEED)
				
		# Check if key is down
		is_down = base.mouseWatcherNode.is_button_down
				
		# Transform the roll value
		if (is_down(self.roll_right_button)):
			self.roll += ROLL_ACCEL * MAX_ROLL_SPEED
		if (is_down(self.roll_left_button)):
			self.roll -= ROLL_ACCEL * MAX_ROLL_SPEED
			
		# Clamp the roll value to the maximum speed values
		self.roll = max(min(self.roll, MAX_ROLL_SPEED), -MAX_ROLL_SPEED)
				
		# Transform the character's orientation by using the turn speed as coordinates
		self.destVec = Vec3(self.mouseXSpeed, self.mouseYSpeed, self.roll * globalClock.getDt())
		self.character.setHpr(self.character, self.destVec)
		
		print self.roll
		return task.cont
			
	def moveControl(self, task):
	
		# Check if key is down
		is_down = base.mouseWatcherNode.is_button_down
	
		# Move the character by incrementing speed values
		# Transform the forward value
		if (is_down(self.forward_button)):
			self.forward += MOVE_ACCEL * MAX_MOVE_SPEED
		if (is_down(self.backward_button)):
			self.forward -= MOVE_ACCEL * MAX_MOVE_SPEED
		if (is_down(self.right_button)):
			self.sides += MOVE_ACCEL * MAX_MOVE_SPEED
		if (is_down(self.left_button)):
			self.sides -= MOVE_ACCEL * MAX_MOVE_SPEED
		if (is_down(self.up_button)):
			self.vert += MOVE_ACCEL * MAX_MOVE_SPEED
		if (is_down(self.down_button)):
			self.vert -= MOVE_ACCEL * MAX_MOVE_SPEED
			
		# Clamp the forward value to the maximum speed values
		self.forward = max(min(self.forward, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
		self.sides = max(min(self.sides, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
		self.vert = max(min(self.vert, MAX_MOVE_SPEED), -MAX_MOVE_SPEED)
		
		# Transform the character's position by using the movement speed as coordinates
		self.destPoint = Point3(self.sides, self.forward, self.vert) * globalClock.getDt()
		self.character.setPos(self.character, self.destPoint)
		
		return task.cont
		
	def externForce(self, task):
		
		# Apply external forces by interpolating the movement speed to 0
		if (self.forward != 0):
			lerpForward = self.forward + (0 - self.forward) * MOVE_DECEL
			self.forward = max(min(lerpForward, 0), lerpForward)
			
		if (self.sides != 0):
			lerpSides = self.sides + (0 - self.sides) * MOVE_DECEL
			self.sides = max(min(lerpSides, 0), lerpSides)
			
		if (self.vert != 0):
			lerpVert = self.vert + (0 - self.vert) * MOVE_DECEL
			self.vert = max(min(lerpVert, 0), lerpVert)
				
		if (self.roll != 0):
			lerpRoll = self.roll + (0 - self.roll) * ROLL_DECEL
			self.roll = max(min(lerpRoll, 0), lerpRoll)
				
		if (self.mouseXSpeed != 0):
			lerpSpeed = self.mouseXSpeed + (0 - self.mouseXSpeed) * TURN_DECEL
			self.mouseXSpeed = max(min(lerpSpeed, 0), lerpSpeed)
			
		if (self.mouseYSpeed != 0):
			lerpSpeed = self.mouseYSpeed + (0 - self.mouseYSpeed) * TURN_DECEL
			self.mouseYSpeed = max(min(lerpSpeed, 0), lerpSpeed)
				
		return task.cont

About firs post: possible you should playing with tasks priority

As for the second post: why not multiplied rolling speed by 10 or whatever suitable coefficient?

The problem is that it should make sense with the current globals I have. Here are the values:

MAX_MOVE_SPEED = 12.0 # 48.0 * 0.25
MAX_TURN_SPEED = 2.4
MAX_ROLL_SPEED = 1.8
MAX_BANK_ANGLE = 30.0
MOVE_ACCEL = 0.0625
MOVE_DECEL = 0.05
TURN_ACCEL = 0.0625
TURN_DECEL = 0.05
ROLL_ACCEL = 0.0625
ROLL_DECEL = 0.05
BANK_ACCEL = 0.03
BANK_DECEL = 0.03

Now, I won’t multiply the rolling speed by a higher number in some arbitrary fashion if I don’t get what it’s doing, because these values have worked someplace else. There must be something in my Panda3D code that isn’t getting me the results I want. I’ll remind you of what happened:

  • without delta time the character rolls so fast that it creates after-images;
  • with delta time the character rolls so slow that it feels static.

What unit is Roll, by the way?

I mean use multiplying here

self.destVec = Vec3(self.mouseXSpeed, self.mouseYSpeed, self.roll * globalClock.getDt()*MY_MUL_COEFF)

By default Panda uses degrees if I remember.

Ah, wait! I used this and it worked without disabling sync-video. Thanks very much! It still doesn’t explain the roll feature being too slow, but at least it’s not going too fast. It’s appropriate now, so thank you!

Hi again. I have a new problem, which is related to Yaw Banking, or the infamous Turn Roll. It consists of rolling to the side when turning by an amount no bigger than MAX_BANK_ANGLE (30.0) and then rolling back to zero whenever we aren’t turning, which means deltaX (mouse input) is going to play a role in defining both the Heading and Roll of the character. I suspect that Pitch is also going to be affected, in order to keep the Turn Roll leveled with the starting Pitch.

I’ve tried many things, but some of them feel like cheating, like using the camera’s orientation to provide the effect. As you can imagine, the effect will only work in First Person View, and not when behind the character from a distance. I already have a means to clamp the Bank value to 30.0 even if the mouse input is superior, but what should I do from now on?

Should I lerp the character’s orientation to this value until the value itself becomes 0? Help would be appreciated.

The missing part has question marks assigned to a variable. It’s the only thing left for me to complete this.

def turnRolling(self, task):
		
		# Use the bank value to subtract the turn roll value and increment
		# the character's roll coordinate
		if (self.bank != 0):
			lerpBank = self.bank + (self.turnRoll - self.bank) * BANK_ACCEL
			self.turnRoll = lerpBank
		else:
			lerpBank = self.bank + (self.turnRoll - self.bank) * BANK_DECEL
			self.turnRoll = lerpBank
		
		self.roll = ???
		
		print self.roll
		return task.cont

Not quite understand what happens, it would be great if you showed it on the video or scheme or something so. In any case - for the 6DoF I suggest to use quaternions instead of euler angles, which have known restrictions.

https://www.youtube.com/watch?v=K5kfTqNJ_vw - Here’s an example video. Notice how the ship rolls a bit to the side when it turns.

Yes, for this type of motion quaternions would be best solution im my opinion. The problem with Euler angles is that it depends from rotation order, for example heading on 45 deg. and hten pitching on 45 deg. is not the same as pitching on 45 deg. and then heading on 45 deg. Quaternions not have this issue.

I can make example with quats a bit later if you wish.

Another variant - you can attach model to another “avatar” parent node, and just roll model while main control will be over the “avatar” and it will have normal rotation.

http://sourceforge.net/p/d2x-xl/code/HEAD/tree/trunk/physics/physics.cpp#l173

I’ve found this page and it has what I need, I just don’t know what “fixed angle” and “max_roll” are in its context, so that I can apply them to mine.