Pointentity Problem
Blitz3D Forums/Blitz3D Programming/Pointentity Problem
| ||
I recently started a little game in 3D as an exercise (I'm way more comfortable in 2D), and right now it's best thought of as a 2D-in-3D (baby steps, here) shooter, with an increasing Y position moving things upwards on the screen and an increasing X moving things to the right. Using placeholder meshes (CreateCone() mostly), I tried to make homing missiles. That's where my problems begin. I remember reading somewhere on these forums that in 3D, PointEntity can be used effectively for homing missiles, and curvier flight paths can easily be implemented by adjusting the optional roll parameter. Yet no matter how I try it, nothing works. My first problem is that PointEntity doesn't point the point of the cone mesh I'm using, but rather a point on the side. I sort of overcame this with liberal usage of the RotateMesh command, but it still isn't pretty. Instead of only the roll value determining how much it turns (because I effectively only want the missiles to rotate on the XY plane), it changes - as per the command description - both pitch and yaw values until the missile is somewhat pointing towards it, but the whole thing's rotated like crazy in a direction I don't want. I've tried rotating my meshes so that different axes are switched, and through tinkering in this manner I eventually got PointEntity to point the tip of the cone directly at the target (roll value useless), which seems to be the best I'll ever get. Does anyone have any ideas/examples/pseudocode? Any help would be much appreciated. |
| ||
Have you tried parenting a pivot at the tip of the cone? Or, for less hassle, use positionmesh to make the tip of the cone at 0,0,0. That way, when point entity is used, it will point at the tip of the cone. Remember, point entity, points an entity at the 0,0,0 local position of a mesh, it's centre basically. PositionMesh, move the vertices of the mesh away from the centre in your case. If that fails, can you post some code to demonstrate your problem :o) |
| ||
Cones are created pointing upwards along the positive Y axis. PointEntity points the entity along the positive Z axis. You will need to use RotateMesh to rotate the cone 90 degrees on the X axis for it to be pointing in the correct direction. eg. RotateMesh cone, 90.0, 0.0, 0.0 As I understand it, the roll parameter of PointEntity just rotates the source entity on its local Z axis, which is probably not something you are going to need. Global timer = CreateTimer( 25 ) Graphics3D 800, 600, 0, 2 SetBuffer BackBuffer() Global cam = CreateCamera() CameraZoom cam, 1.6 MoveEntity cam, 2.0, 2.0, -10.0 Global light = CreateLight() Global cone = CreateCone() RotateMesh cone, 90.0, 0.0, 0.0 UpdateNormals cone Global cube = CreateCube() UpdateNormals cube PositionEntity cube, 20.0, 10.0, 40.0 While Not KeyHit( 1 ) MoveEntity cube, -0.22, 0.0, 0.0 PointEntity( cone, cube ) MoveEntity cone, 0.0, 0.0, 0.3 UpdateWorld RenderWorld Flip WaitTimer( timer ) Wend End |
| ||
you might want to check out Deltayaw and deltapitch they give you the amout of degrees an entity needs to turn in order to point at another. i had a similar question a few months back. the post from StevieG in this thread has a nice function he made try it out. http://www.blitzbasic.com/Community/posts.php?topic=75492#843596 |
| ||
This is where ... http://www.blitzbasic.com/codearcs/codearcs.php?code=1927 comes in handy. You can do similar to point entity using aligntovector also. Cheers Stevie |
| ||
Here's an example using AlignToVector.Global vx#, vy#, vz# Global not_collided = True Global timer = CreateTimer( 25 ) Graphics3D 800, 600, 0, 2 SetBuffer BackBuffer() Global cam = CreateCamera() CameraZoom cam, 1.6 MoveEntity cam, 2.0, 2.0, -10.0 Global light = CreateLight() Global cone = CreateCone() RotateMesh cone, 90.0, 0.0, 0.0 UpdateNormals cone Global cube = CreateCube() UpdateNormals cube PositionEntity cube, 20.0, 10.0, 40.0 While Not KeyHit( 1 ) If EntityDistance( cube, cone ) < 1.0 HideEntity cone EntityColor cube, 255.0, 0.0, 0.0 not_collided = False EndIf If not_collided MoveEntity cube, -0.22, 0.0, 0.0 ;PointEntity( cone, cube ) vx# = EntityX( cube, True ) - EntityX( cone, True ) vy# = EntityY( cube, True ) - EntityY( cone, True ) vz# = EntityZ( cube, True ) - EntityZ( cone, True ) AlignToVector( cone, vx#, vy#, vz#, 3, 0.15 ) MoveEntity cone, 0.0, 0.0, 0.31 EndIf UpdateWorld RenderWorld Flip WaitTimer( timer ) Wend End |
| ||
Thanks a lot, everyone. I knew I could've done it with some math and TurnEntity, but I thought PointEntity might've provided an easier way. Apparently not. Thanks for suggesting the AlignToVector technique. Works like a charm. |
| ||
Uh oh. Here's a problem that's currently got me stumped: This game is best described as a top-down 2D spaceship game, and the ships move via waypoints. AlignToVector works perfectly in this setting, except for in one rare scenario. If the waypoint is set exactly (pixel-perfect) in a straight line behind the ship (see crude ASCII-illustration)... /_\ w OR w |> ...then the ship, instead of turning left or right, turns backwards into the Z axis to get to the waypoint. As the entire game occurs on the xy plane, this is a very serious problem, and I have found that after this occurs, no amount of waypointing will correct the ship and bring it back onto the plane. Curiously enough, this occurs even though I'm using AlignToVector with the vector_z# variable set to zero. I've tried correcting this with if statements, but they can only reasonably work if the ship is facing either directly up, down, left, or right, and - though I have yet to produce it - I am positive that the effect could occur when the ship is facing any angle in between as well. The if statements don't work for other reasons as well that I don't want to explain in this post, as they don't seem relevant right now. Basically, I'm wondering: is there any relatively simple solution that will keep my ships in the xy plane at all times and still allow me to use AlignToVector to bring my ships to waypoints. I could of course hard-code a function bringing ships to waypoints with angles and TurnEntity or something like that, but AlignToVector is much simpler and probably more efficient, and I'd really like to stick with it if I can. Any ideas? |
| ||
I'm not sure if this would mess up the way you have set up your code but here is thisPositionEntity entity,EntityX(entity),EntityY(entity),0 Basicly, it lets it move around on the x and y axis but stays at 0 on the z axis. |
| ||
That works, but - predictably - the ship turns into the Z axis without moving, until it has turned a full 180 degrees. That's like seeing the Asteroids ship try to turn towards the screen. For a 2D game, it simply doesn't make sense. Any other ideas? |
| ||
I think what your getting is a singularity where align to vector does not know which way to turn. You could use turnentity 0,0,( .0001 or -.0001 ) just before your aligntovector call in this situation to give it a helping hand. Alternatively, use the deltaroll code I posted. Stevie |
| ||
Stevie, that wouldn't work either without if statements... what if the TurnEntity call positioned the ship to create the singularity? Examining the vector values used in AlignToVector, I could create some if statements to try to do that only during this singularity... except that the same values occur in other scenarios as well... I think I'll try implementing some deltaroll-ish code in a day or so. Just wondering Stevie, what do you use for those helicopter homing missiles in Polymaniacs? I'd imagine that AlignToVector in that scenario would be more efficient, as the odds of generating the singularity (already difficult to do in 2D) would be virtually impossible in 3D, as every possible target would have to be directly (floating-point precision) behind the missile, and not moving. |
| ||
Of course you would only use a slight turn if you knew that the singularity was going to occur before you used aligntovector. Is that really such a hardship?function MYaligntoVector( entity, vx#, vy#, axis = 1, rate# = 1 ) if vx = 0 and vy < 0 turnentity entity, 0,0,-.0001 aligntovector entity, vx, vy, 0, axis, rate# end function With the deltaroll function you will only ever return -180 or 180 when the target is directly behind the missile so it will work perfectly. Trust me, I have had to use it myself when doing 2d in 3d on a single plane. Alternatively just use the x/z plane and deltayaw - it's six and half a dozen. BTW, I use aligntovector on the z axis for those missiles. |
| ||
Except from what I found, it wasn't always when vx = 0 and vy < 0. For instance, what if the ship is facing East? Then vy would be zero, and vx < 0. And what if the ship's at an angle? Not for me. I'll try deltaroll, thank you very much! |
| ||
Oops - my bad - your right on th vx, vy .. if you put these lines in first ... function MYaligntoVector( entity, vx#, vy#, axis = 1, rate# = 1 ) tformvector vx, vy, 0, 0, entity if tformedx()=0 and tformedy() < 0 turnentity 0,0,-.0001 aligntovector entity, vx, vy, 0, axis, rate# end function |
| ||
A 'YawToEntity' function. Seems to work well enough. Another possible use for this is in turning bots to face a waypoint entity in a 3D (or 2D in 3D) game.Global timer = CreateTimer( 25 ) Graphics3D 800, 600, 0, 2 SetBuffer BackBuffer() Global cam = CreateCamera() CameraZoom cam, 1.6 MoveEntity cam, 0.0, 60.0, 0.0 TurnEntity cam, 90.0, 0.0, 0.0 Global light = CreateLight() Global cone = CreateCone() RotateMesh cone, 90.0, 0.0, 0.0 UpdateNormals cone PositionEntity cone, 0.0, 0.0, -20.0 Global cube = CreateCube() UpdateNormals cube PositionEntity cube, 40.0, 0.0, 20.0 While Not KeyHit( 1 ) MoveEntity cube, -0.22, 0.0, 0.0 YawToEntity( cone, cube, 0.5 ) ;PointEntity( cone, cube ) ;MoveEntity cone, 0.0, 0.0, 0.3 UpdateWorld RenderWorld Text 10, 10, DeltaYaw( cone, cube ) Flip WaitTimer( timer ) Wend End Function YawToEntity( src_entity, dest_entity, rate# ) ; Turns 'src_entity' to point at 'dest_entity' at the rotation rate specified by 'rate#'. Local target_yaw# = DeltaYaw( src_entity, dest_entity ) ; If the required correction amount is less than the correction amount to be applied... If Abs( target_yaw# ) < rate# ; Point 'src_entity' directly at 'dest_entity' to prevent jittering. TurnEntity src_entity, 0.0, target_yaw#, 0.0 Else ; Turn 'src_entity' gradually towards 'dest_entity'. TurnEntity src_entity, 0.0, rate# * Sgn( target_yaw# ), 0.0 EndIf End Function |
| ||
Yes, but we're talking about the XY plane rather than the XZ plane so you can't use deltayaw. This does the same thing but on the XY plane .. Function POINT( Source , Target, Rate#=1.0 ) TFormPoint 0,0,0 , Target, Source DeltaRoll# = vectoryaw( tformedx(), 0 , tformedy() ) if abs( DeltaRoll ) > Rate turnentity Source, 0,0,DeltaRoll * Rate else turnentity Source, 0,0,DeltaRoll endif End Function |
| ||
Thanks, everyone. I'll dabble and tell you what seems to work the best. |
| ||
This is a slightly more complex test of the code I posted above. One interesting thing I found when targetting stationary objects with missiles, is that if the turn rate is not responsive enough in relation to the collision radius of the target, you can get the missile locked into an orbit around the target. Global timer = CreateTimer( 25 ) Graphics3D 800, 600, 0, 2 SetBuffer BackBuffer() Global cam = CreateCamera() CameraZoom cam, 1.6 MoveEntity cam, 0.0, 60.0, 0.0 TurnEntity cam, 90.0, 0.0, 0.0 Global light = CreateLight() Global cone = CreateCone() RotateMesh cone, 90.0, 0.0, 0.0 UpdateNormals cone PositionEntity cone, 0.0, 0.0, -20.0 Global cube = CreateCube() UpdateNormals cube ;PositionEntity cube, 40.0, 0.0, 20.0 PositionEntity cube, Rnd( -30.0, 30.0 ), 0.0, Rnd( -30.0, 30.0 ) While Not KeyHit( 1 ) ;MoveEntity cube, -0.22, 0.0, 0.0 If EntityDistance( cone, cube ) < 2.0 PositionEntity cube, Rnd( -30.0, 30.0 ), 0.0, Rnd( -30.0, 30.0 ) EndIf YawToEntity( cone, cube, 10.0 ) MoveEntity cone, 0.0, 0.0, 0.5 UpdateWorld RenderWorld Flip WaitTimer( timer ) Wend End Function YawToEntity( src_entity, dest_entity, rate# ) ; Turns 'src_entity' to point at 'dest_entity' at the rotation rate specified by 'rate#'. Local target_yaw# = DeltaYaw( src_entity, dest_entity ) ; If the required correction amount is less than the correction amount to be applied... If Abs( target_yaw# ) < rate# ; Point 'src_entity' directly at 'dest_entity' to prevent jittering. TurnEntity src_entity, 0.0, target_yaw#, 0.0 Else ; Turn 'src_entity' gradually towards 'dest_entity'. TurnEntity src_entity, 0.0, rate# * Sgn( target_yaw# ), 0.0 EndIf End Function |
| ||
Thanks, Bill. Yes, I had that problem with my old method too, but I believe that as long as the ship's moving and turning at the same time, there's no way to fix the orbiting that I can think of. Right now, however, my code doesn't use any collisions... it's all EntityDistance stuff. Any ideas? Does anyone know how other games fix this problem? |
| ||
This code makes the missile a bit smarter. If the distance and angle relative to the target indicate that an orbit lock is likely, it will disable turning until it has enough distance on the target to get lined up on it. Note that I'm still using the xz plane here. The yaw rotational functions are available without the need for bodges, and the EntityYaw function returns more sensible values than the 'get value' functions for the other axiis. This code can be used for open space combat, where there's no ground plane for the smart missile to worry about This code can be used for ground based combat, where you don't want the missile plowing into the ground plane. The missile only disables turning if it is facing 'upwards'. |
| ||
If you add a bit of momentum to the missile it seems to hit the target eventually ...Graphics3D 640,480,16,1 Global CAMERA = CreateCamera() PositionEntity CAMERA, 0,0,-100 ;50,0 ;RotateEntity CAMERA, 90,0,0 Global TARGET = CreateCube() ScaleEntity TARGET, 2,2,2 EntityColor TARGET, 255,0,0 Type MissileT Field Mesh Field Flame Field Vx#, Vy# Field Acceleration# Field Momentum# Field Turnrate# End Type Global MISSILE.missileT = MISSILEcreate( .01, .99, .2 ) ;init target PositionEntity TARGET, Rand(-50,50 ), Rand( -50,50 ) ,0 While Not KeyDown(1) ;target reached or spawn new target position If KeyDown( 57 ) Or EntityDistance( MISSILE\mesh, TARGET ) < 6 PositionEntity TARGET, Rand(-50,50 ), Rand( -50,50 ) , 0 EndIf MISSILEupdate() RenderWorld() Flip Wend End ;=========================================================== ;=========================================================== ;=========================================================== Function MISSILEcreate.MissileT( Acceleration# , Momentum#, TurnRate# ) ;max speed = Acceleration / ( 1- Momentum ) m.missileT = New missileT m\Acceleration = Acceleration m\Momentum = Momentum m\TurnRate = TurnRate m\Mesh = CreateCone() FitMesh m\Mesh, -1,0,-1,2,6,2 m\Flame = CreateCone( 8 , False, m\Mesh ) RotateMesh m\Flame, 0,0,-180 FitMesh m\Flame,-1,-4,-1,2,4,2 EntityColor m\Flame, 255,128,0 Return m End Function ;=========================================================== ;=========================================================== ;=========================================================== Function MISSILEupdate() For m.missileT = Each missileT ;turn POINT( m\Mesh, TARGET, m\TurnRate ) ;move TFormVector 0,m\Acceleration, 0,m\Mesh, 0 m\Vx = m\Vx * m\Momentum + TFormedX() m\Vy = m\Vy * m\Momentum + TFormedY() TranslateEntity m\Mesh, m\Vx, m\Vy, 0 ;animate flame ScaleEntity m\Flame, 1,Rnd( .5, 5 ), 1 Next End Function ;=========================================================== ;=========================================================== ;=========================================================== Function POINT( Source , Target, Rate#=1.0 ) TFormPoint 0,0,0 , Target, Source DeltaRoll# = VectorYaw( TFormedX(), 0 , TFormedY() ) If Abs( DeltaRoll ) > Rate TurnEntity Source, 0,0,DeltaRoll * Rate Else TurnEntity Source, 0,0,DeltaRoll EndIf End Function |
| ||
Thanks guys, I'll test some things out hopefully today. |
| ||
Okay, I've been trying to implement Stevie's DeltaRoll stuff, but I just can't seem to get it to work. I'm not trying to do any momentum stuff here, just simple turning and moving forwards. But when I implement it, the value for DeltaRoll always seems to be 90 or -90, and when I have the ship turn each frame, even a slight amount, based on this DeltaRoll value, the sign for DeltaRoll changes every frame, effectively keeping the ship always facing the same direction, regardless of where its target is. |
| ||
Buggy, no offence mate but it works in the code I posted above so you're clearly doing something different. If you need any further help you need to start posting code to show what your doing 'cos we are not mind readers. |
| ||
This is some code I knocked up to display the values returned by the EntityPitch, EntityYaw, and EntityRoll functions. It illistrates a few points, which I wanted to get clear in my head: 1 - EntityPitch returns the same values in different quadrants. 2 - It's important to be clear on where the starting rotation (0.0 degrees) is, and which direction the rotation will turn the entity in relation to the axis you are viewing along, as well as where the 'flipover' points (plus to minus, etc) are in the rotation. 3 - I have way too much time on my hands. |
| ||
I recently started a little game in 3D as an exercise (I'm way more comfortable in 2D), and right now it's best thought of as a 2D-in-3D (baby steps, here) shooter, with an increasing Y position moving things upwards on the screen and an increasing X moving things to the right. But when I implement it, the value for DeltaRoll always seems to be 90 or -90 +90 & -90 are the pitchangle max min so it looks to me as if you're using the XZ axis rather than XY axis which you originally asked for. |
| ||
Hmm... it's possible that in trying different ideas, I somehow accidentally switched axes. Thanks a lot guys for your continued help. |
| ||
Yup... I just checked and somewhere amidst all of the fiddling, I had added some RotateNormals and RotateEntity commands that were screwing things up... but I didn't notice because moving entities along each axis moved them the way they were supposed to, and pitch/yaw/roll worked normally as well. Without trying to wrap my mind around how that happened, I'm just glad that everything works now, including Stevie G's DeltaRoll stuff and Bill Stanbrook's smart-missile idea. Thanks a lot, guys! |
| ||
Stevie G - Your DeltaRoll function works like a charm, but like a charm it feels like magic, magic that I can't seem yet to divine. How does "TFormPoint 0, 0, 0, Target, Source" produce different TFormedX and Y values as the Source rotates? I really don't understand how 2 absolute points can be compared in this command in order to get a rotation angle for a Source that can be pointing in any direction to point to a Target entity (doesn't matter which way this entity points). But as I already said, it works, so what voodoo does it do? Along with the above question, I'm also confused by any TForm command that doesn't use an entity as the source and 3D space (i.e., 0) as the destination which seems to be the only examples in the Blitz help docs...seems like the following (potentially more complex?) permutations are also possible: 1) entity, entity (as in your example above and related to this posting) 2) 3D world, entity (as in some other examples I've seen) 3) 3D world, 3D world (I assume this is possible?) Any clarification on these would allow me to keep at least the rest of my hair, and, of course, thanks for all your past help in the forum. |
| ||
All the tformpoint 0,0,0,Target, Source does is finds out what position the Target is relative to the Source in the Sources local coords. Using the tform commands means that scale and rotation of the source are taken into consideration. I use the tform commands alot and they are probably the most useful commands in blitz for me. There are loads of uses, too many to list ... (2) Applying pseudo fiction to a road tyre. Say Vx, Vy and Vz represent the world velocity of the tyre. Clearly a tyre can be moving in a different direction to the way it's pointing. To get the velocity relative to the direction the tyre is pointing use : tformvector Vx#, Vy#, Vz#, 0 , TyreMesh Then, to reduce the velocity on the lateral and longitudinal directions relative to the direction of the tyre .. Vx# = tformedx() * .75 Vy# = tformedy() Vz# = tformedz() * .99 Then to get the new vx, vy , vz in world coords ... tformvector Vx, Vy, Vz, TyreMesh, 0 Vx# = tformedx() Vy# = tformedy() Vz# = tformedz() (3) World to World This is pretty pointless as the assumption with world coords is that there is no rotation or scale. Is there anything specific you don't understand? Stevie |
| ||
Stevie - Thank you for your response. You've given me something to chew on with respect to the other permutations...I am still confused about the TFormPoint command though. TFormVector and TFormNormal give results (at least with the examples in Blitz help docs TForming an entity to 3D space) that are intuitive to me. On the other hand, I'm not sure what TFormPoint is telling me (with the resulting TFormedX, etc. commands). It seems to make sense when the ship is pointing up at zero roll. It seems to be telling me the ship is in the target's local coordinates - basically a vector from the target to the ship. But as soon as the ship rotates and does not move it's position, the TFormedX, etc. commands change values (specifically the X and Y versions in the code following program authored originally by you I think (Hit space to change target location and hit G to rotate a bit): Graphics3D 1280,1024,32,1 Global Camera = CreateCamera() : PositionEntity Camera, 0, 0, -30 Global Ship = CreateCone(): ScaleMesh Ship, 1, 2, 1 : EntityColor Ship, 0,0,255 ;: PositionEntity Ship, +0.000, +0.000, +10.000 Global Target = CreateCube() : EntityColor Target,255,0,0 WireFrame True Repeat If KeyHit( 57 ) PositionEntity target, Rand(-20,20 ), Rand(-20,20 ), 10 EndIf DR# = DELTAroll#( Ship, Target ) If KeyHit( 34 ) TurnEntity ship, 0, 0, DR# * 0.10 End If RenderWorld() Text 10,10,"Delta Raw: "+DR# Text 10,30,"TFormedX: "+TFormedX() Text 10,40,"TFormedY: "+TFormedY() Text 10,50,"TFormedZ: "+TFormedZ() Text 10,70,"Target X: "+EntityX( target ) Text 10,80,"Target Y: "+EntityY( target ) Text 10,90,"Target Z: "+EntityZ( target ) Text 10,110,"Ship X: "+EntityX( ship ) Text 10,120,"Ship Y: "+EntityY( ship ) Text 10,130,"Ship Z: "+EntityZ( ship ) Flip Until KeyDown(1) ;================================================================================= ;================================================================================= ;================================================================================= Function DELTAroll#( Source , Target ) TFormPoint 0,0,0 , Target, Source Return VectorYaw ( TFormedX() , 0 , TFormedY() ) End Function So after running the above and hitting spacebar once, the target will move and provide a vector within the TFormed commands in order to get from the target to the ship. Once G is hit to rotate the ship a bit, the TFormed commands produce different values even though neither entity actually moved locations. Perhaps the TFormed results are derived from 2 components - relative location and angle of rotation even though 0,0,0 is used in it? To me, it seems that angle of rotation would only come into play with respect to the TFormed commands if non-zeros were used in the TFormPoint command. So I suppose, I'm just not sure what the TFormPoint does and what it is trying to tell the uninitiated through it's TFormedX,Y, and Z commands. None of the Blitz help docs for TFormPoint addressed angle of rotation. I apologize for the long winded-ness fo the above...thanks for your help! |
| ||
The changing tformed results are expected behaviour. You're rotating the source so the target object will be in a different position relative to it's position and rotation. In the exmple .. tformpoint 0,0,0,Target, Source If I were to change this to 0,0,10 then the point in world space would be 10 units in front of the target. Note that the Targets rotation would now come into play. By using 0,0,0 this ensures the point in world space is exactly where the Target entity is and the Targets rotation is irrelevant here. Make sense? |
| ||
Thanks again... There's nothing, it seems, in the Blitz help docs for TFormPoint that indicates that it can be used in this manner so maybe I'm missing the general point of what TFormPoint does (perhaps I ony understand it's most basic application)...I understand the example where 0,1,0 in local space can be something totally different in world space due to rotation and scaling, but in this case there is no vector provided for all coords are zero (0,0,0). Regardless, I was able to get the code to work in my project so again I thank you. |