Camera control. I'm going bald...

Blitz3D Forums/Blitz3D Programming/Camera control. I'm going bald...

... With all the hair-pulling this conundrum's induced in me. =P

Okay, so, I'm trying to figure out 3rd person camera control. The player moves freely through the levels, and the camera rotates smoothly around the player. The current control system uses the 200/3/5/8 arrow keys.

Now, here's the deal: I've found a lot of movement & camera tracking examples, but all of them use the same system: Up to move forward, while Left & Right TURN the player entity left & right. I need a standard, console-style, free-range control system, like you might get in Spyro, Dr. Muto, Mario64, all that. I found a Mario64 demo in the codearcs, but it still used that same L/R turn method.

Basically, I need the following system:

-Up makes the player move away from the camera

-Down makes the player turn 180 & move towards the camera

-Left & Right make the player turn 90/-90 & move to the left or right screen edge, with the camera following

-WASD control the camera itself, allowing it to move around the player

Of course, no matter what way the player's facing, the camera is always moving to get behind the player. This means that, for instance, if you hold down Left, the player will travel to the left, but also in a curve, eventually ending up where they started from.

I've looked everywhere; the codearcs, forums, tutorial section, I've even done advanced searches. Nothing. Any ideas? I REALLY need some help, since I've now tried everything I can think of. Thanks!

did u try:

while not keyhit(1)

if keydown(203)

if entityyaw(cam) >= 45 and entityyaw(cam) <= 90 then y# = y# + 1

else

if keydown(205)

if entityyaw(cam) > 90 and entityyaw(cam) <= 360 then y# = y# - 1

endif

endif

rotateentity cam, x#, y#, z#

updateworld
renderworld

flip

wend

end


?

it doesnt work. but u get the idea :)

good luck on ur project! :)

~DS~

Thanks DSW; yeah, I've tried that, I've tried pivot methods, I've tried AlignToVector()... I'm out of ideas by now. T_T

Thanks anyway!

Try creating a pivot and making the player chase the pivot with a bit of slack, same philosphy as a chasecam. You'll have two chases going on...the player chasing the pivot and the camera chasing the player.

Tried that. My last method used two pivots, one for the camera, and one for its target position. The camera's target was a child of the player's mesh entity, and positioned at 0,5,-13 relative to it. The camera was always pointing at the player, while the camera's pivot was always pointing at, and moving towards, the camera's target position.

That method worked fine for following the player, but when it came to actually turning the player, things got crazy. It always made the camera come too close to the player, thus causing the player to turn too tightly... I dunno. I'm totally lost by now.

Here's the closest I got to achieving my goal:

CountGfxModes()
CountGfxModes3D()


Graphics3D 640,480,16,1


;[Block] Tweening stuff
	Const TargetFrameRate = 60
	Global TwElapsed,TwPeriod,TwTicks,TwTime,TwTween#,UpdateLoops
	TwPeriod = 1000 / TargetFrameRate
	TwTime = MilliSecs() - TwPeriod
;[End block]
Global Wire


Const LevelColl = 1,SonicSoulColl = 2,SonicWheelColl = 3
Collisions SonicSoulColl,LevelColl,2,3
Collisions SonicWheelColl,LevelColl,2,3


Global Gravity#


Global Ground = LoadTerrain("gfx\heightmap_256.bmp")
ScaleEntity Ground,1000/TerrainSize(Ground),70,1000/TerrainSize(Ground)
TerrainDetail Ground,1000,True
TerrainShading Ground,True
PositionEntity Ground,-500,-100,-500

Global MossTex = LoadTexture("tex\terrain-1.jpg")
ScaleTexture MossTex,32,32
EntityTexture Ground,MossTex
EntityType Ground,LevelColl


Global SonicSoul = CreateSphere()
Global SoulTex = CreateTexture(64,64)
SetBuffer TextureBuffer(SoulTex)
Color 0,0,255
Rect 0,0,64,64
Color 255,0,0
Line 0,0,0,63
Line 15,0,15,63
Line 31,0,31,63
Line 47,0,47,63
Line 0,31,63,31
EntityTexture SonicSoul,SoulTex
RotateEntity SonicSoul,0,0,0
;ScaleEntity SonicSoul,2,2,2
;PositionEntity SonicSoul,0,2,0
EntityType SonicSoul,SonicSoulColl
;EntityAlpha SonicSoul,.5
;EntityFX SonicSoul,16;Flags: No backface culling
;FlipMesh SonicSoul

Global PrevYaw# = EntityYaw(SonicSoul)
Global wheels[4]

Local Cnt=1,X#,Z#
For Z=1 To -1 Step -2
	For X=-1 To 1 Step 2
		wheels[Cnt]=CreateSphere(8,SonicSoul)
		EntityAlpha wheels[Cnt],.5
		ScaleEntity wheels[Cnt],.5,.5,.5
		EntityRadius wheels[Cnt],.5
		PositionEntity wheels[Cnt],X,0,Z
		EntityType wheels[Cnt],SonicWheelColl
		Cnt=Cnt+1
	Next
Next


Global MainCameraTarget = CreatePivot(SonicSoul)
PositionEntity MainCameraTarget,0,5,-13
;PositionEntity MainCameraHinge,0,4,0
Global MainCameraPivot = CreatePivot();MainCameraHinge)
Global MainCamera = CreateCamera(MainCameraPivot)
;PositionEntity MainCamera,0,4,0
CameraViewport MainCamera,0,0,GraphicsWidth(),GraphicsHeight()
CameraClsColor MainCamera,0,128,255


Global MainLight = CreateLight()




SetBuffer BackBuffer()
SeedRnd MilliSecs()


While Not KeyHit(1)


Cls


If KeyHit(17) Then Wire = Not Wire
WireFrame Wire


Repeat
	TwElapsed = MilliSecs() - TwTime
Until TwElapsed


TwTicks = TwElapsed / TwPeriod
TwTween = Float(TwElapsed Mod TwPeriod) / Float(TwPeriod)


For UpdateLoops = 1 To TwTicks
	TwTime = TwTime + TwPeriod
	If UpdateLoops = TwTicks Then CaptureWorld
	CORE()
Next


RenderWorld TwTween


Color 255,255,255
Rect 0,0,100,100
Color 0,0,0
Text 0,0,EntityYaw(MainCamera)
Text 0,15,EntityYaw(SonicSoul)


Flip


Wend


End


Function CORE()


UpdateCamera()
UpdateSonicsSoul()


UpdateWorld


End Function


Function UpdateCamera()


;TurnEntity MainCameraHinge,(EntityPitch(SonicSoul) - EntityPitch(MainCameraHinge)) / 50,(EntityYaw(SonicSoul) - EntityYaw(MainCameraHinge)) / 50,(EntityRoll(SonicSoul) - EntityRoll(MainCameraHinge)) / 50
;PositionEntity MainCameraHinge,EntityX(SonicSoul),EntityY(SonicSoul),EntityZ(SonicSoul),True
;MoveEntity MainCameraHinge,0,(EntityY(SonicSoul) - EntityY(MainCameraHinge)) / 10,0
PointEntity MainCameraPivot,MainCameraTarget
;Keep camera in range
If EntityDistance(MainCameraPivot,MainCameraTarget) < 5
	MoveEntity MainCameraPivot,0,0,EntityDistance(MainCameraPivot,MainCameraTarget) / 50
Else
	MoveEntity MainCameraPivot,0,0,EntityDistance(MainCameraPivot,MainCameraTarget) - 5
EndIf
PointEntity MainCamera,SonicSoul


End Function


Function UpdateSonicsSoul()


PrevYaw = EntityYaw(SonicSoul)


;Align Sonic's "soul" to his "wheels"... Oh my GAWD, that sounds so weird!!! @_@
Local ZX# = (EntityX(wheels[2],True) + EntityX(wheels[4],True)) / 2
ZX=ZX - (EntityX(wheels[1],True) + EntityX(wheels[3],True)) / 2
Local ZY# = (EntityY(wheels[2],True) + EntityY(wheels[4],True)) / 2
ZY=ZY - (EntityY(wheels[1],True) + EntityY(wheels[3],True))/2
Local ZZ# = (EntityZ(wheels[2],True) + EntityZ(wheels[4],True)) / 2
ZZ=ZZ - (EntityZ(wheels[1],True) + EntityZ(wheels[3],True)) / 2
AlignToVector SonicSoul,ZX,ZY,ZZ,1

ZX = (EntityX(wheels[1],True) + EntityX(wheels[2],True)) / 2
ZX = ZX - (EntityX(wheels[3],True) + EntityX(wheels[4],True)) / 2
ZY# = (EntityY(wheels[1],True) + EntityY(wheels[2],True)) / 2
ZY = ZY - (EntityY(wheels[3],True) + EntityY(wheels[4],True)) / 2
ZZ# = (EntityZ(wheels[1],True) + EntityZ(wheels[2],True)) / 2
ZZ = ZZ - (EntityZ(wheels[3],True) + EntityZ(wheels[4],True)) / 2
AlignToVector SonicSoul,ZX,ZY,ZZ,3

RotateEntity SonicSoul,EntityPitch(SonicSoul),PrevYaw,EntityRoll(SonicSoul)

;resposition wheels
Local Cnt = 1,Z#,X#
For Z = 1 To -1 Step -2
	For X = -1 To 1 Step 2
	;	PositionEntity wheels[cnt],0,0,0
	;	ResetEntity wheels[cnt]
		PositionEntity wheels[Cnt],X,-1,Z
		Cnt=Cnt+1
	Next
Next


If KeyDown(200);Up
	If KeyDown(203);Left
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) + 45,EntityRoll(SonicSoul)
	ElseIf KeyDown(205);Right
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) - 45,EntityRoll(SonicSoul)
	Else
		RotateEntity SonicSoul,EntityPitch(SonicSoul),PrevYaw,EntityRoll(SonicSoul)
	EndIf
	MoveEntity SonicSoul,0,0,.3
ElseIf KeyDown(208);Down
	If KeyDown(203);Left
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) + 135,EntityRoll(SonicSoul)
	ElseIf KeyDown(205);Right
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) - 135,EntityRoll(SonicSoul)
	Else
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) + 180,EntityRoll(SonicSoul)
	EndIf
	MoveEntity SonicSoul,0,0,.3
Else
	If KeyDown(203);Left
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) + 90,EntityRoll(SonicSoul)
		MoveEntity SonicSoul,0,0,.3
	ElseIf KeyDown(205);Right
		RotateEntity SonicSoul,EntityPitch(SonicSoul),EntityYaw(MainCamera,True) - 90,EntityRoll(SonicSoul)
		MoveEntity SonicSoul,0,0,.3
	Else
	EndIf
EndIf


If EntityCollided(SonicSoul,LevelColl)
	Gravity = 0
Else
	If Gravity > -1 Then Gravity = Gravity - .005
	TranslateEntity SonicSoul,0,Gravity,0
EndIf


End Function

You can see for yourself how it won't work. I used the wheel code from Mark Sibly's "driver.bb" demo, in the B3D samples. The terrain heightmap and texture are from the same demo.


This demo from Stevie G is pretty close to what I need:

Graphics3D 640,480,16,1

Global LIGHT = CreateLight()
Global PLANE = CreatePlane() 
EntityColor PLANE, 200,100,100
texture = CreateTexture( 32, 32 )
SetBuffer TextureBuffer( texture )
For y = 0 To 1
	For x = 0 To 1
		Color 100, 150+25*( ( x+y) Mod 2 ) , 100
		Rect x*16,y*16,16,16,1
	Next
Next
SetBuffer BackBuffer()
ScaleTexture texture, 50,50
EntityTexture PLANE, texture
FreeTexture texture

Global CUBE = CreateCube() : FitMesh CUBE, -2,0,-2,4,10,4 
Tmp = CreateCube() : FitMesh Tmp, -1,7,2,2,2,2 : AddMesh Tmp, CUBE : FreeEntity Tmp
Global TARGET = CreatePivot()

Global CAMERApivot = CreatePivot()
Global CAMERA = CreateCamera( CAMERApivot )
PositionEntity CAMERA, 0,50,-50
PointEntity CAMERA , CAMERApivot

While Not KeyDown(1)

	PositionEntity CAMERApivot , EntityX( CUBE ) , 0, EntityZ( CUBE )
	TurnEntity CAMERApivot, 0 , KeyDown(31) - KeyDown(30) , 0
	
	;move relative to camera
	Mx# = KeyDown(205) - KeyDown(203)
	Mz# = KeyDown(200) - KeyDown(208 )

	If Mx <> 0 Or Mz <> 0
		;move relative to camera
		TFormVector Mx, 0, Mz , CAMERApivot , 0
		TranslateEntity CUBE, TFormedX(), 0, TFormedZ()
		;turn relative to movement
		PositionEntity TARGET, EntityX( CUBE ) + TFormedX() , 0, EntityZ( CUBE ) + TFormedZ()
		TurnEntity CUBE, 0 , DeltaYaw( CUBE, TARGET ) * .25, 0
	EndIf
	
	RenderWorld()
	Flip

Wend


The only problem here is that the camera stays statically in whatever position it's placed by the user. In other words, if you turn in this demo, the camera doesn't move back behind you as I need it to.

U mean like a Shadow of the Colossus cam?

Erm... Maybe? Heh, I've never played Shadow of the Colossus. Some games that I know of that use this common (I think) camera system are:

-Jak & Daxter, Jak II & III
-Spyro 1, 2 & 3
-Sly Cooper
-Dr. Muto


That's all I can think of off the top of my head right now, but I know there are others; a lot of free-roaming console games use it.

I changed the SuperCam function I found on blitch just a tiny bit...

It follow and turns like you would want it to.
For the first three parameters you need a
Camera
Pivot for the camera (not parented)
Point is the thing you follow, the entity

Function Camera_Follow(camera,camera_pivot,point,camera_speed#,distance#,height#,xrot#,tilt#)
	Local cx#,cy#,cz#
	Local TempYaw# = EntityYaw(camera)
	
	TFormPoint 0,height#,-distance#,point,0
	cx = (TFormedX() - EntityX(camera)) * camera_speed#
	cy = (TFormedY() - EntityY(camera)) * camera_speed#
	cz = (TFormedZ() - EntityZ(camera)) * camera_speed#
	TranslateEntity camera_pivot,cx,cy,cz
	PositionEntity camera,EntityX(camera_pivot,1),EntityY(camera_pivot,1),EntityZ(camera_pivot,1),1
	TurnEntity camera,0,DeltaYaw(camera,point)*.3,0
	RotateEntity camera,xrot#,EntityYaw(camera),tilt#
End Function


and to move your Point/entity in the right direction after you turn it you can use this
TFormVector 0,0,.1,Point,0	
X# = X + TFormedX()
Z# = Z + TFormedZ()

TranslateEntity Point,X,0,Z,1


I kind of know what the TFormVector does, it's just harder to explain it than it is to use it. You input how far you want to go, into the x,y,z parameters. The numbers that you get in the TFormedX,Y,Z commands, depends on which way you are facing. It splits the numbers between each TFormed command to make you move in a certain direction when you use TranslateEntity.

Thanks GIA... I'm not sure exactly where to put this in my program, but I'll try to figure it out & give it a try!

you put it under

wend
end

and u call it

in while not keyhit(1)

graphics3d 800,600,0,2

piv=createpivot()

cam=createcamera(piv)

OR

cam=createcamera()

piv=createpivot(cam)

point=0.1 ;?

speed# = .1

distance# = .1;?

height# = .4

xrot# = entitypitch(cam)
tilt# = .1

while not keyhit(1)

Camera_Follow(cam,piv,point,camera_speed#,distance#,height#,xrot#,tilt#)

updateworld()
renderworld()

flip

wend

end

Function Camera_Follow(camera,camera_pivot,point,camera_speed#,distance#,height#,xrot#,tilt#)
Local cx#,cy#,cz#
Local TempYaw# = EntityYaw(camera)

TFormPoint 0,height#,-distance#,point,0
cx = (TFormedX() - EntityX(camera)) * camera_speed#
cy = (TFormedY() - EntityY(camera)) * camera_speed#
cz = (TFormedZ() - EntityZ(camera)) * camera_speed#
TranslateEntity camera_pivot,cx,cy,cz
PositionEntity camera,EntityX(camera_pivot,1),EntityY(camera_pivot,1),EntityZ(camera_pivot,1),1
TurnEntity camera,0,DeltaYaw(camera,point)*.3,0
RotateEntity camera,xrot#,EntityYaw(camera),tilt#
End Function

hope this helps :)

~DS~

Not sure if this is what you meant, but I wrote this yesterday based on your explaination:
Graphics3D 800, 600, 0, 2
SetBuffer BackBuffer()

plane = CreatePlane() ;create ground
MoveEntity plane, 0, -5, 0
tex = CreateTexture(16,16,9)
Line 8,0,8,16
Line 0,8,16,8
CopyRect 0, 0, 16, 16, 0, 0, BackBuffer(), TextureBuffer(tex)
EntityTexture plane, tex

org = CreateSphere() ;create scenery
HideEntity org
For i = 0 To 100
	nw = CopyEntity(org)
	c = Rand(80, 160)
	s# = Rnd(0.5, 1.5)
	EntityColor nw, c, c, c
	ScaleEntity nw, s, s, s
	PositionEntity nw, Rand(-100, 100), Rand(-100, 100), Rand(-100, 100)
Next
	
;create main pivot
main = CreatePivot()

;base pivots for camera rotation
cambase = CreatePivot(main) ;to maintain camera in same direction as cone
campiv = CreatePivot(cambase) ;to be able to turn camera
;setup camera
cam = CreateCamera(campiv)
MoveEntity cam, 0, 5, -10
PointEntity cam, main

;create red dot (should be pivot)
aim = CreateSphere(8, main)
ScaleEntity aim, 0.1, 0.1, 0.1
EntityColor aim, 255, 0, 0
;place it in front of main
PositionEntity aim, 0, 0, 1

;create cone, and attach to main
cone = CreateCone(8, 1, main)
RotateMesh cone, 90, 0, 0

;create chase point
chase = CreatePivot(cone)
MoveEntity chase, 0, 0, 5

Repeat

	;wasd = turn/move camera
	If KeyDown(17) Then MoveEntity cam, 0, 0.1, 0: PointEntity cam, main
	If KeyDown(31) Then MoveEntity cam, 0, -0.1, 0: PointEntity cam, main
	If KeyDown(30) Then TurnEntity campiv, 0, -1, 0 
	If KeyDown(32) Then TurnEntity campiv, 0, 1, 0

	;arrow keys = place 'aim'
	If KeyHit(203) Then rx = -1 rz = 0
	If KeyHit(205) Then rx = 1 rz = 0
	If KeyHit(200) Then rx = 0 rz = 1
	If KeyHit(208) Then rx = 0 rz = -1
	
	;arrow keys = move object
	If KeyDown(203) Or KeyDown(205) Or KeyDown(200) Or KeyDown(208) Then
		TranslateEntity main, rx*0.1, 0, rz*0.1
	End If
	
	;place aim and gradually pointentity cone to aim
	PositionEntity aim, rx, 0, rz
	dpt# = DeltaPitch(cone, aim) * 0.1
	dyw# = DeltaYaw(cone, aim) * 0.1
	TurnEntity cone, dpt, dyw, 0
	
	;keep camera in position relative to object
	TurnEntity cambase, 0, DeltaYaw(cambase, chase) * 0.05, 0

	RenderWorld
	Flip
	
Until KeyHit(1)

End


Geez, Warner, you actually wrote a whole program for me?! Thank you SO MUCH!

Anyway, that's almost it, except for one thing: the cone doesn't keep turning. In other words, when you hit, for instance, Left, the cone turns to the left. But if you keep holding Left, the cone doesn't keep turning; instead, it keeps moving in the same direction, until the camera is behind it, making it look like it's moving forward (which should only happen when Up is pressed.)

I know what you want. It's also throughout various N64 games. I guess it just needs a bit more of a technical description: you want the player to move left or right but in relation to the camera's rotation. Front or Back will always be towards\away from camera (not just positive Z, negative Z), and Left and Right will always be related to the camera's Local X axis, and not only will he turn in that direction but also walk in it.
So, for knowing the direction you'd have to go when the user presses Left, you'd use something like...
Speed#=2.0
TFormVector -Speed,0,0,Camera,0 ;We are using "-Speed" because the user is pressing LEFT.

MoveEntity Player,TFormedX(),TFormedY(),TFormedZ()


RotateEntity Player,EntityPitch(Player,True),EntityYaw(Camera,True)-90,EntityRoll(Player,True) 

;The above should turn the player in the left direction in relation to the camera, albeit a little 
;snappy. You'd need to give it a smoothing factor for it to be smoothier. 
;(something like (EntityYaw(Camera,True)-EntityYaw(Player,True)*0.1-90)

In this system, if the player keeps the Left key pressed down, for example, the character would constantly be walking in a complete circle counter-clockwise. The camera would be sitting in the middle of this circle [not that it is relevant, but the radius of this circle would be the distance of the player to the camera].

Hmmmm... *strokes chin where beard would be*

This might be just what I'm looking for, Kryzon; I'll try this ASAP! Thanks!!!

Great! I'm just not sure about the RotateEntity part, you'll have to work a bit there.
But for the rest, i'm positive it's the same effect as in console games: he's walking using the camera as reference, as "origin".

You'll also have to incorporate one of those camera schemes that are around the code archives (also in the Castle sample).

Also, you'll need different TFormVector checks for each of the KeyDowns (Up,Down,Left,Right - like, for down, for example, you'd use TFormVector 0,0,-Speed,Camera,0, going on the negative Z using the camera as reference), as I only showed how you'd do it using the Left key.

(I remember playing some N64 games, and when you were moving your character forward and then turned the camera around him using the C-Buttons, for example, he'd go forward but on another direction - you could even control his direction just by moving him forward constantly and then turning the camera to where you wanted him to go)

Good luck, and make sure to inform us of your success!

Oh, believe me, when this game's done, everyone here'll be the first to know! It's not a marketable game, just a fan work, but still... ^_^