Code archives/3D Graphics - Misc/Mario64 Style Camera Control
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| After searching the NET for clues, asking for help, and studying Mario64 for ages, I came up with this player code. For the download of the whole code, please get this from here Basically, I have had 3rd Person Code samples before (Including Castle demo) but they do not seem to emulate the smooth readjustment of the camera to fall back behind the player after a set period of time (In this case half a second). Also, not all cameras allow the player to run towards you. This code emulates the control used in Mario64, Bugs bunny - Lost In Time and Rayman. Again, I am sure it can be VASTLY improved, so please send your improvements and I'll keep updating this. | |||||
; Mario64 style camera and player control
; by Rob (Speccyman) Pearmain
; rob.pearmain@ioko365.com
; http://www.peargames.com
; 11th February 2003
; After asking for lots of help and searching the NET I could not find
; code that acurately emultated Mario64 control.
; This is my attempt, and can be vastly improved no doubt, please feel
; free to modify.
; I studied Mario64 carefully on my N64 emulator, and decided that the
; best control was on the level where he jumps into the picture and has
; to meet the bombs etc.
; The theory is, when Mario runs forwards, the camera follows. When he runs
; back the camera is pushed back viewing Mario running towards you.
; When you turn left or right, you slowly turn around the camera pivot.
; IMPORTANT. When you take your finger off a key, the camera auto corrects
; itself around the back of Mario, in a smooth rotating way, NOT, flying through
; the player mesh or messing up the camera angles.
; When you run forward, you run in the direction THE CAMERA is facing at the time
; ------------------------------------------------------------------
; Game's frames-per-second setting
; ------------------------------------------------------------------
Global gameFPS = 60
; ------------------------------------------------------------------
; Open 3D display mode
; ------------------------------------------------------------------
Graphics3D 640, 480
; The player consists of a pivot to control which direction he is
; moving, and a mesh which is the model. The model rotates independently
; of the pivot to achieve the smooth turn to new direction functionality
; that exists in Mario64
global oPlayerPivot = createpivot()
entityradius oPlayerPivot,1
entitytype oPlayerPivot,1
global oPlayerMesh = LOADANIMMESH("mariorun.x")
; Change these settings depending on your mesh
scaleentity oPlayerMesh,.2,.2,.2
animate oPlayermesh,1,1
; The targetpivot will always sit in the same position as the player pivot
; but will rotate independently, that is why it is not a child.
; The camera will aim for the TargetOrbitPivot.
global oTargetPivot = createPivot()
global otargetorbitpivot = createpivot(oTargetPivot)
; Depending on how far you move the orbit pivot from the center it will
; effect settings on smoothing the camera and speed, experiment carefully.
; Here, I move it 15 units in fromt of the player. It will always remain
; 15 units away from the player.
moveentity otargetorbitpivot,0,5,15
; Finally, we create the camera pivot, and attached a real camera to it.
global oCameraPivot = createpivot()
global cam = CreateCamera ()
; IMPORTANT, set the height of the camera
;positionentity cam,0,10,0
; Milliseconds to wait after keypress for camera to adjust
; Set this to 0 for no pause
Const CAMERAPAUSE=500
Global waitbeforecameraadjust=millisecs()
; Counter for jump
Global nJump#
; ------------------------------------------------------------------
; Use double buffered animation (automatic, but I prefer this!)
; ------------------------------------------------------------------
SetBuffer BackBuffer ()
; ------------------------------------------------------------------
; General setup
; ------------------------------------------------------------------
; Set up a plane surface so we can see that we are running
plane=createplane()
tex=loadtexture("grass.jpg")
scaletexture tex,12,12
entitytexture plane,tex
positionentity plane,0,-1,0
entitytype plane,2
collisions 1,2,2,2
; ------------------------------------------------------------------
; Frame limiting code setup
; ------------------------------------------------------------------
framePeriod = 1000 / gameFPS
frameTime = MilliSecs () - framePeriod
fps=0
framecount=0
oldfps=0
Repeat
; --------------------------------------------------------------
; Frame limiting
; --------------------------------------------------------------
Repeat
frameElapsed = MilliSecs () - frameTime
Until frameElapsed
frameTicks = frameElapsed / framePeriod
frameTween# = Float (frameElapsed Mod framePeriod) / Float (framePeriod)
; --------------------------------------------------------------
; Update game and world state
; --------------------------------------------------------------
For frameLimit = 1 To frameTicks
If frameLimit = frameTicks Then CaptureWorld
frameTime = frameTime + framePeriod
UpdateGame ()
fps = fps + 1
UpdateWorld
Next
; --------------------------------------------------------------
; **** Wireframe for DEBUG only -- remove before release! ****
; --------------------------------------------------------------
If KeyHit (17): w = 1 - w: WireFrame w: EndIf ; Press 'W'
; --------------------------------------------------------------
; Draw 3D world
; --------------------------------------------------------------
RenderWorld frameTween
; --------------------------------------------------------------
; Show result
; --------------------------------------------------------------
text 0,32,"Frames per second = " + oldfps
if millisecs()-framecount > 1000 then framecount=millisecs():oldfps=fps:fps=0
Flip
Until KeyHit (1)
End
; ------------------------------------------------------------------
; Game update routine, called from frame limiting code
; ------------------------------------------------------------------
Function UpdateGame ()
; Put the TargetPivot and Player Model at the same spot as the Player's Pivot
positionentity oTargetPivot,entityx(oPlayerPivot),entityy(oPlayerPivot),entityz(oPlayerPivot)
positionentity oPlayermesh,entityx(oPlayerPivot),entityy(oPlayerPivot),entityz(oPlayerPivot)
positionentity cam,entityx(oCameraPivot),entityy(oCameraPivot),entityz(oCameraPivot)
; Point the camera to the orbitng target, and the camera always at the player
pointentity oCameraPivot,otargetorbitpivot
pointentity cam,oPlayerPivot
; --------------------------
; CONTROL
; --------------------------
; The following moveentity settings have been tweaked for all the other settings for pivots, experiment with care
key=0
; Left
if keydown(203) then rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)+90),0 : key=1
; Right
if keydown(205) then rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)-90),0 : key=2
; Forward (Move camera forward also)
if keydown(200) then
; Check to see if Left or Right has been pressed, if so move diagonally
if key > 0
if key = 1
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)+45),0
else
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)-45),0
end if
else
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)),0
end if
key=3
end if
; Back (Move camera back slightly faster)
if keydown(208) then
; Check to see if Left or Right has been pressed, if so move diagonally
if key > 0
if key = 1
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)+135),0
else
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)-135),0
end if
else
rotateentity oPlayerPivot,0,wrapANGLE(Entityyaw#(cam,true)+180),0
end if
key=4
moveentity oCameraPivot,0,0,-.35
end if
if keyhit(57)
if entitycollided(oPlayerPivot,2)
nJump#=njump#+3
key=5
end if
end if
translateentity oPlayerPivot,0,nJump#,0
if nJump# > -.5 then nJump# = nJump#- .5
if not entitycollided(oPlayerPivot,2) then key=5
; Reset adjust timer and move player
if key>0 then
waitbeforecameraadjust = millisecs()
moveentity oPlayerPivot,0,0,.5
end if
; ---------------------------
; ADJUST
; ---------------------------
; Here, we compare the YAW of the player's model compared to the direction the pivot is heading, and slowly turn it to
; match. This gives the effect of smooth turning of the model
LWhichWayShouldITurn% = Check_DoesEntityNeedToTurnToTarget(oPlayerMesh,oPlayerPivot)
If lWhichWayShouldITurn% <> 0 Then
If lWhichWayShouldITurn% = 1 Then
TurnEntity oPlayerMesh,0,8,0
End If
If lWhichWayShouldITurn% = -1 Then
TurnEntity oPlayerMesh,0,-8,0
End If
End If
if key=0 and ((millisecs()-waitbeforecameraadjust) > CAMERAPAUSE) then
; If camera is far away from the target, then help the camera by pointing the target at the camera
if entitydistance(oCameraPivot,otargetorbitpivot) > 5 then
pointentity oTargetPivot,oCameraPivot
else
; If near, ignore the camera and move the target in line with the player
diff# = wrapangle((entityyaw#(oTargetPivot,true) - entityyaw#(oPlayerPivot,true)) )
if diff# < 180 then turnentity oTargetPivot,0,2,0
if diff# > 180 then turnentity oTargetPivot,0,-2,0
end if
end if
; Move the camera smoothly towards its target
smoothcam(ocamerapivot,otargetorbitpivot,10)
pointentity cam,oPlayerPivot
End Function
; Cool little funtion to return a positive angle
Function WrapAngle(ang#)
If ang > 359 Then ang = ang - 360
If ang < 0 Then ang = 360 + ang
Return ang
End Function
; Smooth out the camera to its target
Function smoothcam(pivot,target,camspeed)
curx#=EntityX(pivot)
curz#=EntityZ(pivot)
cury#=entityy(pivot)
destx#=EntityX(target,True)
destz#=EntityZ(target,True)
desty#=EntityY(target,True)
curx#=curx#+((destx#-curx#)/camspeed)
curz#=curz#+((destz#-curz#)/camspeed)
cury#=cury#+((desty#-cury#)/camspeed)
PositionEntity pivot,curx#,cury#,curz#
End Function
Function Check_DoesEntityNeedToTurnToTarget(source_pivot,target_pivot)
;=================================================
;parameters:
; source_pivot: is the source pivot that we want to turn toward a target pivot
; target_pivot: is a target pivot where we want the source pivot to rotate to
;Returned values:
; 0 : no turn needed
; 1 : turn left
; -1 : turn right
;memo start angle and end angle, I just consider the integer parts of it, using Floor
s = Floor(EntityYaw(source_pivot)) ;this is the start yaw angle, that is, the current yaw orientation of the source
t = Floor(EntityYaw(target_pivot)) ;this is the end angle, that is, the angle we should reach
If s = t Or Abs(s-t) < 4 Then ;if the two angles are the same we do not need any rotation !
Return 0
EndIf
;the angle goes from 0,180 and 0,-180; now I normalize to 0-360
If s < 0 Then s = 360 + s
If t < 0 Then t = 360 + t
;now we found the right direction where to turn, in order to choose the shortest path:
;check if the difference is greather than 180
If Abs(s-t) > 180 Then
;check if the start angle is greater than the target angle
If s > t Then
Return 1 ;turn left
Else
Return -1 ;turn right
EndIf
Else
;check if the start angle is greater than the target angle
If s > t Then
Return -1 ;turn right
Else
Return 1 ;turn left
EndIf
EndIf
End Function |
Comments
None.
Code Archives Forum