This must be one of the few remaining DBC questions which appears to remain unanswered.
I've seen many examples, but none work 100% correctly - especially when using the
Move Object command (rather than Position Object ObjNum,X,Y,Z). Those that are nearly there suffer from the same problem that my own attempts do (see beow).
I have to confess at the start that I was never any good with trig at school so my maths is being stretched to the limits as it is...
I'd like to suggest that we all chip in a bit of knowledge and communally see if we can crack this problem once and for all.
Object Of The Excercise:
To make the function in the code snippet below work. You should be able to use the cursor keys to move the box on the matrix. As it moves onto a slope, the box should tilt to reflect the angle of the matrix - regardless of the direction you move.
Empty Code Snippet To Start Us Off:
Rem Communal Tilt Project
RemStart
Contributors:
TDK_Man
** Your Name Added Here If You Contribute Anything Used **
RemEnd
Sync On: Sync rate 0
Autocam Off
Hide Mouse
Rem Make A Hill To Climb...
MatNum = 1
Make Matrix MatNum,150,150,6,6
rem Prepare Matrix Texture 1,1,1,1
For Nz = 2 To 4
For Nx = 2 To 4
Set Matrix Height MatNum,Nx,Nz,10.0
Next Nx
Next Nz
Update Matrix MatNum
Rem Make A 'Car'...
ObjNum = 10: ObjWid#=2.0: ObjHig#=1.0: ObjDep#=3.0
Make Object Box ObjNum,ObjWid#,ObjHig#,ObjDep#
Color Object ObjNum,RGB(100,0,0)
rem Position Object ObjNum, 75.0, 0.5, 10.0: Rem Looking North
Position Object ObjNum, 20.0, 0.5, 75.0: Rem Looking East
YRotate Object ObjNum, 90.0
Rem Position Camera...
Position Camera 0,5,-50
Point Camera Object Position X(ObjNum),Object Position Y(ObjNum),Object Position Z(ObjNum)
Rem Main Program Loop...
Do
If LeftKey() = 1 Then YRotate Object ObjNum, WrapValue(Object Angle Y(ObjNum)-1.2)
If RightKey() = 1 Then YRotate Object ObjNum, WrapValue(Object Angle Y(ObjNum)+1.2)
If UpKey() = 1 Then Move Object ObjNum, 0.1
If DownKey() = 1 Then Move Object ObjNum, -0.1
I$ = Inkey$()
If I$ = "n" Then YRotate Object ObjNum, 0
If I$ = "s" Then YRotate Object ObjNum, 180
If I$ = "w" Then YRotate Object ObjNum, 270
If I$ = "e" Then YRotate Object ObjNum, 90
Ox# = Object Position X(ObjNum): Oz# = Object Position Z(ObjNum): Oy# = Get Ground Height(1,Ox#,Oz#)
Position Object ObjNum,Ox#,Oy#,Oz#
Tilt_Object(ObjNum,MatNum,Ox#,Oy#,Oz#,ObjWid#,ObjHig#,ObjDep#)
Set Camera To Follow Ox#, Oy#, Oz#, Object Angle Y(ObjNum), 13.0, Oy#+6.0, 15, 0
Sync
Text 5,5,"FPS: "+Str$(Screen FPS())
Loop
Function Tilt_Object(ObjNum,MatNum,Ox#,Oy#,Oz#,ObjWid#,ObjHig#,ObjDep#)
Rem Between us we need to add the code here to make it work
Rem Note: The parameters are only for example - they can be removed or added to as required
EndFunction
Note: I've added [N]orth, [S]outh, [E]ast and [W]est keys to quickly point the box to the main 0, 180, 90 and 270 degree headings while testing.
My Attempt So Far:
ATanFull(X,Y) will return the angle between two points so my reasoning was that I should define two points - one at the front of the box and one at the back.
If I got the height of the matrix at the front point and the height of the matrix at the back point, subtracting one from the other would give me 'Y' in the above formula.
In the same way, getting the X positions of the front and back points and subtracting one from the other will give me the 'X'.
This works and ATanFull returns the slope angle on the matrix.
But, the box needs to be tilted on two axis, so we also need two points on the left and right of the box. Two ATanFull's then give us the XRotate and ZRotate angles to rotate the box.
The first problem is that as the box turns, the four points do not. After a bit of head scratching and a lot of trial and error, I've fixed that problem. So, as the box rotates, the four test points now keep track with it.
In the snippet below, I've created four tiny cubes and positioned them at the four points - purely as a visual aid. In the final version, these cubes will not be required.
Now for the problem I simply can't get my head around:
If the box is pointing North or South, then all is well. But, when the box is pointing East, rotating the box with either XRotate or ZRotate turns the box in
exactly the same way.
This is totally baffling me and I guess is down to the strange properties of Euler rotation - something else I don't fully understand.
Anyway, I added another object (a cone) and tried to use it to apply rotations to - and then apply them to the box. But that idea failed abysmally - for the same reason! When the box is at 90 degrees (pointing East), even though I can calculate the angle of the slope, I cannot apply it to the box.
My Very Rough Code Snippet So Far:
Sync On: Sync rate 0
Autocam Off
Hide Mouse
Rem Make A Matrix Texture...
CLS RGB(0,200,0): Get Image 1,0,0,255,255
Rem Make A Hill To Climb...
MatNum = 1
Make Matrix MatNum,150,150,6,6
rem Prepare Matrix Texture 1,1,1,1
For Nz = 2 To 4
For Nx = 2 To 4
Set Matrix Height MatNum,Nx,Nz,10.0
Next Nx
Next Nz
Update Matrix MatNum
Rem Make A 'Car'...
ObjNum = 10: ObjWid#=2.0: ObjHig#=1.0: ObjDep#=3.0
Make Object Box ObjNum,ObjWid#,ObjHig#,ObjDep#
Color Object ObjNum,RGB(100,0,0)
rem Position Object ObjNum, 75.0, 0.5, 10.0: Rem Looking North
Position Object ObjNum, 20.0, 0.5, 75.0: Rem Looking East
YRotate Object ObjNum, 90.0
Rem Make 4 Height Markers...
Make Object Cube 1,0.1
Make Object Cube 2,0.1
Make Object Cube 3,0.1
Make Object Cube 4,0.1
Rem Position Camera...
Position Camera 0,5,-50
Point Camera Object Position X(ObjNum),Object Position Y(ObjNum),Object Position Z(ObjNum)
Rem Main Program Loop...
Do
If LeftKey() = 1 Then YRotate Object ObjNum, WrapValue(Object Angle Y(ObjNum)-1.2)
If RightKey() = 1 Then YRotate Object ObjNum, WrapValue(Object Angle Y(ObjNum)+1.2)
If UpKey() = 1 Then Move Object ObjNum, 0.1
If DownKey() = 1 Then Move Object ObjNum, -0.1
I$ = Inkey$()
If I$ = "n" Then YRotate Object ObjNum, 0
If I$ = "s" Then YRotate Object ObjNum, 180
If I$ = "w" Then YRotate Object ObjNum, 270
If I$ = "e" Then YRotate Object ObjNum, 90
Ox# = Object Position X(ObjNum): Oz# = Object Position Z(ObjNum): Oy# = Get Ground Height(1,Ox#,Oz#)
Position Object ObjNum,Ox#,Oy#,Oz#
Tilt_Object(ObjNum,MatNum,Ox#,Oy#,Oz#,ObjWid#,ObjHig#,ObjDep#)
Set Camera To Follow Ox#, Oy#, Oz#, Object Angle Y(ObjNum), 13.0, Oy#+6.0, 15, 0
Sync
Text 10,58,"FPS: "+Str$(Screen FPS())
Loop
Function Tilt_Object(ObjNum,MatNum,Ox#,Oy#,Oz#,ObjWid#,ObjHig#,ObjDep#)
Rem The Calculations...
OAy# = Object Angle Y(ObjNum): DirX# = Sin(OAy#): DirZ# = Cos(OAy#)
Text 500,10,Str$(OAy#)
Rem Get Matrix Height At Front Of Box
FX# = Ox# + (ObjDep#/2.0) * DirX#: FZ# = Oz# + (ObjDep#/2.0) * DirZ#
FrontY# = Get Ground Height(MatNum,FX#,FZ#)
Position Object 1, FX#, FrontY# + ObjHig#, FZ#
Rem Get Matrix Height At Back Of Box
BX# = Ox# - (ObjDep#/2.0) * DirX#: BZ# = Oz# - (ObjDep#/2.0) * DirZ#
BackY# = Get Ground Height(MatNum,BX#,BZ#)
Position Object 2, BX#, BackY# + ObjHig#, BZ#
Rem Get Matrix Height At Left Of Box
LX# = Ox# - (ObjWid#/2.0) * DirZ#: LZ# = Oz# + (ObjWid#/2.0) * DirX#
LeftY# = Get Ground Height(MatNum,LX#,LZ#)
Position Object 3, LX#, LeftY# + ObjHig#, LZ#
Rem Get Matrix Height At Right Of Box
RX# = Ox# + (ObjWid#/2.0) * DirZ#: RZ# = Oz# - (ObjWid#/2.0) * DirX#
RightY# = Get Ground Height(MatNum,RX#,RZ#)
Position Object 4, RX#, RightY# + ObjHig#, RZ#
Rem Set Object Rotation ZYX ObjNum
Rem Set Object Rotation XYZ ObjNum
Rem XATF = WrapValue(Atanfull(FZ#-BZ#, FrontY#-BackY#))
Rem ZATF = WrapValue(Atanfull(LX#-RX#, LeftY#-RightY#))
remstart
If (OAy# > 0.0 And OAy# < 90.0) Or OAy# > 270.0: Rem Upish
Rem XATF = WrapValue(Atanfull(FZ#-BZ#, FrontY#-BackY#)-90)
Rem Xrotate Object ObjNum, XATF
If OAy# > 270.0: Rem Up Leftish
Rem ZATF = WrapValue(Atanfull(LX#-RX#,LeftY#-RightY#)+90)
rem Zrotate Object ObjNum,ZATF-90
Text 420,10,"Up Left "
XATF = WrapValue(XATF-0)
ZATF = WrapValue(ZATF-0)
Endif
If OAy# > 0.0 And OAy# < 90.0: Rem Up Rightish
Rem ZATF = WrapValue(Atanfull(RX#-LX#,RightY#-LeftY#)-90)
XATF = WrapValue(XATF-0)
ZATF = WrapValue(ZATF-0)
Text 420,10,"Up Right "
Rem Zrotate Object ObjNum,ZATF-270
Endif
Endif
If OAy# > 90.0 And OAy# < 270.0: Rem Downish
Rem XATF = WrapValue(Atanfull(BZ#-FZ#, BackY#-FrontY#)+90)
Rem Xrotate Object ObjNum, XATF
If OAy# < 180.0: Rem Down Rightish
XATF = WrapValue(XATF-0)
ZATF = WrapValue(ZATF-0)
Text 420,10,"Down Right "
Rem Zrotate Object ObjNum,ZATF
Endif
If OAy# > 180.0 And OAy# < 270.0: Rem Down Leftish
Text 420,10,"Down Left "
XATF = WrapValue(XATF-0)
ZATF = WrapValue(ZATF-0)
Rem ZATF = WrapValue(Atanfull(LX#-RX#,LeftY#-RightY#)+90)
Rem Zrotate Object ObjNum,ZATF
Endif
Endif
remend
If OAy#=0.0: Rem Up
XATF = WrapValue(Atanfull(FZ#-BZ#, FrontY#-BackY#)-90)
ZATF = WrapValue(Atanfull(LX#-RX#, LeftY#-RightY#)-270)
Xrotate Object ObjNum, XATF
Text 420,10,"Up "
Endif
If OAy#=180.0: Rem Down
XATF = WrapValue(Atanfull(FZ#-BZ#, FrontY#-BackY#)-270)
ZATF = WrapValue(Atanfull(LX#-RX#, LeftY#-RightY#)-90)
Xrotate Object ObjNum, XATF
Text 420,10,"Down "
Endif
Rem *********************** FAULTY HEADING EAST SECTION ********************************
If OAy#=90.0: Rem Right
Rem Set Object Rotation ZYX ObjNum
Rem XATF = WrapValue(Atanfull(LZ#-RZ#, LeftY#-RightY#)-90)
Rem Calc Z Angle of slope and place in ZATF...
ZATF = ABS(Atanfull(FX#-BX#, FrontY#-BackY#)-90)
Xrotate Object ObjNum, WrapValue(ZATF)
Text 420,10,"Right "
Rem Set Object Rotation XYZ ObjNum
Endif
Rem ************************************************************************************
If OAy#=270.0: Rem Left
XATF = WrapValue(Atanfull(FZ#-BZ#, FrontY#-BackY#)-270)
ZATF = WrapValue(Atanfull(LX#-RX#, LeftY#-RightY#)-90)
Text 420,10,"Left "
Endif
remstart
If OAy# <= 90 Or OAy# >= 270: Rem Pointing North
XATF = WrapValue(Atanfull(Back#-Front#,FZ#-BZ#))
Xrotate Object ObjNum, XATF
Else: Rem Pointing South
Rem XATF = WrapValue(Atanfull(Front#-Back#,BZ#-FZ#))
Rem Xrotate Object ObjNum, XATF
Endif
If OAy# >= 180: Rem Pointing West
Rem ZATF = WrapValue(Atanfull(BX#-FX#,Back#-Front#)-180)
Rem ZATF = Wrapvalue(Atanfull(Left#-Right#,FX#-BX#)-180)
Rem Zrotate Object ObjNum, ZATF
Else: Rem Pointing East
ZATF = WrapValue(Atanfull(FX#-BX#,Front#-Back#)-90)
Zrotate Object ObjNum,ZATF
Rem ZATF = Wrapvalue(Atanfull(Right#-Left#,BX#-FX#)-180)
rem Zrotate Object ObjNum, Wrapvalue(Atanfull(Left#-Right#,FX#-BX#))
Endif
rem Set Object Rotation XYZ ObjNum
remend
rem Text 10,10,"Front: "+Str$(FrontY#)+" Back: "+Str$(BackY#)+" F-B: "+Str$(FrontY# - BackY#)+" X ATF: "+Str$(XATF)
rem Text 10,26,"Left: "+Str$(LeftY#)+" Right: "+Str$(RightY#)+" L-R: "+Str$(LeftY# - RightY#)+" Z ATF: "+Str$(ZATF)
Text 10,10,"X ATF: "+Str$(XATF)
Text 10,26,"Z ATF: "+Str$(ZATF)
Position Object ObjNum,Ox#,Oy#+(ObjHig#/2.0),Oz#
Rem Front# Rear# Left# Right# Back#-Front# Front#-Back# FX#-BX# BX#-FX#
EndFunction
Note: This snippet is my rough workings and is a bit of a shambles. It has lots of remmed out sections - left in to give you an idea of what I was trying to do.
The section I can't figure out is highlighted 'FAULTY HEADING EAST SECTION' and is only called when the Y angle of the box is set to 90. You can quickly set it to 90 by pressing the E key.
As you can see when you run it, the Z slope angle is calculated (ZATF), but when applied to the box, it rotates on the world X axis whether you use XRotate or ZRotate.
I was using floats for XATF and ZATF, but rounding errors caused problems so I switched to floats. The rotation angles don't actually need float precision and it's smooth enough with integers.
Please feel free to use anything I have done (if it's of any use) and tweak it to make a working function in the communal snippet at the top.
Or, if your expertise is more in maths than programming, you can attempt to explain where I am going wrong.
Hopefully we can get a working function we can all find a use for - especially fr future DBC challenges - which what got me started on this in the first place...
TDK