DrakeTheDragon Posted October 21, 2018 Share Posted October 21, 2018 Well, first off, this weekend I devised an approach to distance gauging similarly to real life sonar or infrared approaches. I'm casting a spell from an invisible activator from below my feet down to the ground (another invisible activator 200 units further down actually, but the projectile passes that one and goes on until it hits the ground or an obstacle),then I'm regularly searching for the obstacle (GetFirstRef, GetNextRef loop, with additional checks for the "right" projectile),then I track it and continuously store the distance it traveled (GetProjectileDistanceTraveled), until it hits the ground/vanishes (IsFormValid + not IsRefDeleted),then the last stored distance traveled is the distance to ground/obstacle (or sufficiently close to it). It's working splendidly under lab conditions,a visible activator following me around in short distance and above my head, casting the spell on command, and I quickly get the result every time. But under gaming conditions, me actually flying around, at a higher speed, with constant scans running with a small delay, it often takes ages for even the projectile to be found, and then ages more until it hits the ground.While actively flying around the distance to ground updates roughly once every 5 minutes, which, needless to say, is less than ideal (read: horribly useless). if 0 < fDistanceCheckDelay let fDistanceCheckDelay -= GetSecondsPassed else if 0 == iDistanceCheckStep let fDebugPosX := fPosX let fDebugPosY := fPosY let fDebugPosZ := fPosZ - 50 rDistanceCaster.SetPos X fDebugPosX rDistanceCaster.SetPos Y fDebugPosY rDistanceCaster.SetPos Z fDebugPosZ let fDebugPosX := fPosX let fDebugPosY := fPosY let fDebugPosZ := fPosZ - 250 rDistanceTarget.SetPos X fDebugPosX rDistanceTarget.SetPos Y fDebugPosY rDistanceTarget.SetPos Z fDebugPosZ rDistanceCaster.Cast DrakeDragonDistanceCheckSpell rDistanceTarget PrintToConsole "%n (%.0f %.0f %.0f) casted sonar spell down towards %n (%.0f %.0f %.0f)" rDistanceCaster fPosX fPosY fPosZ rDistanceTarget fDebugPosX fDebugPosY fDebugPosZ let iDistanceCheckStep := 1 elseif 1 == iDistanceCheckStep if 0 == rDistanceProjectile PrintToConsole "find projectile down" let rDistanceProjectileTemp := GetFirstRef 34 1 Label if rDistanceProjectileTemp if (rDistanceCaster == rDistanceProjectileTemp.GetProjectileSource) && (DrakeDragonDistanceCheckSpell == rDistanceProjectileTemp.GetMagicProjectileSpell) let rDistanceProjectile := rDistanceProjectileTemp else let rDistanceProjectileTemp := GetNextRef Goto endif endif if rDistanceProjectile let fDistanceCheckTimeout := 0 else let fDistanceCheckTimeout += GetSecondsPassed endif if 2 < fDistanceCheckTimeout let rDistanceProjectile := 0 let iDistanceCheckStep := 0 PrintToConsole "projectile down find timeout" endif endif if (IsFormValid rDistanceProjectile) && (0 == IsRefDeleted rDistanceProjectile) let fDistanceBottomTemp := rDistanceProjectile.GetProjectileDistanceTraveled PrintToConsole "projectile down found, current distance: %.2f" fDistanceBottomTemp if 2000 < fDistanceBottomTemp let fDistanceBottomTemp := 2000 let rDistanceProjectile := 0 endif else let rDistanceProjectile := 0 endif if 0 == rDistanceProjectile && 0 < fDistanceBottomTemp let fDistanceBottom := fDistanceBottomTemp let fDistanceBottomTemp := 0 let iDistanceCheckStep := 2 endif else let fDistanceCheckDelay := fDelayForDistanceCheck let iDistanceCheckStep := 0 endif endif I already contemplated slapping a Kalman filter onto it all to smoothen it out, but a little (read: massive) bit faster scanning to begin with would still be preferred. Isn't there any more performant/faster approach to achieve the same thing? It takes way too long for the projectile to even come into being, before it can be tracked.So, if anybody'd know of a better alternative, it'd be more than appreciated. Thanks in advance! Link to comment Share on other sites More sharing options...
QQuix Posted October 22, 2018 Share Posted October 22, 2018 I once had this problem and used GetLOS to detect the distance, with pretty good results (Youtube video) (code). If memory serves, I used two micro actors, actor A near the player and actor B positioned at a distance and checked if A can see B. The nice thing about it is that you don't need to position actor B gradatively farther. You can start from a random distance (previous distance would be a good start) and move it farther or closer as necessary and quickly find the distance with a few successive approximations. Again, if memory serves, using GetLOS from the player does not work well in third person view, so I used actor A near the player.I used actors because I read somewhere that GetLOS is not CPU efficient with inanimate objects. Once more, if memory serves, there is a maximum distance beyond which GetLOS will always return false. Link to comment Share on other sites More sharing options...
DrakeTheDragon Posted October 22, 2018 Author Share Posted October 22, 2018 Oh, yeah, I was already considering giving a GetLOS-based approach a try next. But now I got all the facts and won't even have to start from scratch anymore. Thank you very much! I do recall having seen this video before. Man, does it look smooth indeed. That's definitely whole leagues faster than any spell or arrow projectile flies. I'll give it a go right up next. I knew GetLOS will go through actors like in your video from the WiKi talk, but that won't be a problem for me at all. It's distance to ground, not distance to the next dumbo staying in your way while you fly, after all, and I don't expect there to be much updrift coming from a person/creature standing around.The limit in range will also not be a problem to me. I'm already limiting it to 2000 units top, as only at distances closer than that it starts making sense to check. "Oh, hey! There's some rock mass at around 5 miles below you. Quick! Start gaining height or you'll collide!"... Na-ah. And for all those occasional/random occurrences of it temporarily failing, for whatever reason, I could still slap a Kalman filter onto it at any time, if need be, to reduce the noise. Can't wait to try it out now! Link to comment Share on other sites More sharing options...
QQuix Posted October 22, 2018 Share Posted October 22, 2018 I checked the code in my files and I do not see some things I do remember, like the two actors I mentioned.My code will not help much, as it is pre-OBSE (still uses SaveIP and GoTo as loop control).I probably exercised GetLOS further some other time later. The nice thing about GetLOS is that you can move the target and check its visibility several times in the same frame. But it has a limit. If memory serves, after around 20 times, it starts to return erratic results. I clearly remember implementing some control to limit the number of loops per frame to avoid this glitch, interrupting the probing and continuing in the next frame(s). Another thing I remember is that Oblivion ground is at different depths, depending on the detection method. For collision, it is where it seems to be, but for visibility it is about one foot deeper, as can be noticed in the video when targeting the ground. Link to comment Share on other sites More sharing options...
DrakeTheDragon Posted October 27, 2018 Author Share Posted October 27, 2018 Hmm, I can't seem to get it to work.Maybe it's because your checks have been into the direction the player's looking, while mine are straight down, and making a creature look down (via SetAngle X) doesn't seem to stick really, nor does keeping it in place via SetPos. I'm keeping the source creature in place right under my feet and facing down constantly (or that's the plan), as the quest script currently runs all 0.001 seconds.The target creature is put into place at increasing distance in steps of 200 units and no farther away than 2000 units. So it should be only 10 steps, i.e. 10 GetLOS calls, max. per frame. It correctly reports a 0 distance while I'm no further than 200 units from ground. But as soon as I get above that height it continuously starts spamming either 0 as the result of the loop or 2000, as it supposedly sees the target at all 10 steps, even while already deep behind a street mesh. Not even once did it see the target at only a part of the 2000 range, always all or none, and it's continuously alternating. Here's the code I'm currently using: if 1 == checkdistance if 0 == DragonDistanceSource.GetInSameCell player DragonDistanceSource.MoveTo player endif if 0 == DragonDistanceTarget.GetInSameCell player DragonDistanceTarget.MoveTo player endif let posXsrc := player.GetPos X let posYsrc := player.GetPos Y let posZsrc := player.GetPos Z let posXtrg := posXsrc let posYtrg := posYsrc let posZtrg := posZsrc - 200 Label 2 if posZsrc - posZtrg <= 2000 DragonDistanceSource.SetPos X posXsrc DragonDistanceSource.SetPos Y posYsrc DragonDistanceSource.SetPos Z posZsrc DragonDistanceSource.SetAngle X 90 DragonDistanceSource.SetAngle Y 0 DragonDistanceSource.SetAngle Z 0 PrintToConsole "LOS source Z: %.2f" posZsrc DragonDistanceTarget.SetPos X posXtrg DragonDistanceTarget.SetPos Y posYtrg DragonDistanceTarget.SetPos Z posZtrg PrintToConsole "LOS target Z: %.2f" posZtrg if DragonDistanceSource.GetLOS DragonDistanceTarget let losdistance := posZtrg - posZsrc let losdistance := Abs losdistance PrintToConsole "source sees target at distance: %.4f" losdistance let posZtrg -= 200 Goto 2 endif endif ;DragonDistanceSource.MoveTo DragonDistanceHoldingMarker ;DragonDistanceTarget.MoveTo DragonDistanceHoldingMarker PrintToConsole "LOS distance: %.4f" losdistance endif What's also quite unexpected, is that the creatures "both" seem to constantly drop down, but their distance increases, as coded, and at a certain distance (I couldn't yet figure out what exactly's the cause or when/where it happens) they just switch back. And to confuse the hell out of me, all the time the script still states them at the exact positions they should be at, not at all where I "see" them. And I also don't see them ever looking down, despite me directly setting their angle via script.They seem to randomly spin around though, so maybe that's why they see the target intermittently. Link to comment Share on other sites More sharing options...
QQuix Posted October 28, 2018 Share Posted October 28, 2018 (edited) Puzzling, as the code seems OK to me. Some comments... What's also quite unexpected, is that the creatures "both" seem to constantly drop down, but their distance increases, as coded, and at a certain distance (I couldn't yet figure out what exactly's the cause or when/where it happens) they just switch back. And to confuse the hell out of me, all the time the script still states them at the exact positions they should be at, not at all where I "see" them. One thing I deduced along the years is that the engine has a 'Script' phase, a 'Physics' and a 'Rendering' phase, one after the other in each frame. This accounts for the difference between the script position and the visual position: (1) you position the actor in the script phase, (2) in the physics phase the engine applies the gravity pull and moves the actor down and (3) in the rendering phase the actor is rendered in this new position. I remember my first attempts tries with flying actors where I kept them in the air with SetPos and the result was a heavy flickering up and down.([EDIT] Maybe SetVelocity would solve the visuals, but it did not exist at the time.) In my esp, I used a dog scaled down to the size of a flea, so I could not really see it to notice any movement. Also, during the script phase there is no physics involved, so you may use SetPos just once before the loop, And I also don't see them ever looking down, despite me directly setting their angle via script.They seem to randomly spin around though, so maybe that's why they see the target intermittently. I don't think so. As mentioned above, by the time the engine renders the creature, the GetLOS computation has long been done. I do not remember if setting angleX forces the actor to move the head. Maybe it doesn't. The "Look" function certainly does. Maybe it's because your checks have been into the direction the player's looking, while mine are straight down, Yes, maybe. But I suppose you have tried with the player looking down, which would solve the problem (if this was the problem). The Wiki says that the field of view for GetLOS is 95 degrees. I'm keeping the source creature in place right under my feet and facing down constantly The only practical suggestion I have is that you place the source creature a little below the player Z position. Maybe the player himself is considered in the way. For debugging purpose, you might keep the loop running always 10 times and printing the results each time. Something like this: Label 2 if posZsrc - posZtrg <= 2000 PrintToConsole "LOS source Z: %.2f" posZsrc DragonDistanceTarget.SetPos X posXtrg DragonDistanceTarget.SetPos Y posYtrg DragonDistanceTarget.SetPos Z posZtrg PrintToConsole "LOS target Z: %.2f" posZtrg let losdistance := posZtrg - posZsrc let losdistance := Abs losdistance if DragonDistanceSource.GetLOS DragonDistanceTarget PrintToConsole "source sees target at distance: %.4f" losdistance else PrintToConsole "source does not see target at distance: %.4f" losdistance endif let posZtrg -= 200 Goto 2 endif Edited October 28, 2018 by QQuix Link to comment Share on other sites More sharing options...
Moktah Posted October 29, 2018 Share Posted October 29, 2018 going out on a limb herebut when you give the command for....DragonDistanceSource.SetAngle X 90 does it keep adding the 90 to that axis each time the code fires off?as in....first time it runs it adds 90 degrees and makes it look downthen you added 90 again and now its looking upthen you added 90 again and now it looking down again looking closer at the wiki for GetLOS"The field of view for LOS is +/- 95 degrees from the 0 axis line in which the observer (non-player actor, player, or camera) is looking straight ahead."I may be thinking of this wrong, but looks like you don't need to set your 'activator' to 'look down' since its already 'looking' for anything up to 95 degrees anyway. Link to comment Share on other sites More sharing options...
QQuix Posted October 29, 2018 Share Posted October 29, 2018 No. SetPos and SetAngle are absolute values. In this case, it sets the angle to 90 every time. Link to comment Share on other sites More sharing options...
Moktah Posted October 29, 2018 Share Posted October 29, 2018 found this that may be of interesthttps://cs.elderscrolls.com/index.php?title=Determining_Ground_Position Link to comment Share on other sites More sharing options...
DrakeTheDragon Posted October 29, 2018 Author Share Posted October 29, 2018 Ah, yeah, I've read that article/discussion before. Don't know if the "set actor below ground, wait a frame, then it'll be moved up the on-ground position automatically" approach will make much sense at the speed I'm flying at. But them talking of water reminds me, the other methods will all fail when above water, so I'll have to check distance to water plane as well anyways. There's also an OBSE function now to obtain ground mesh height at a specific position, but the ground mesh isn't the "only" geometry that can make up the ground, especially not when inside a city and the street model is floating leagues above ground (see IC). I basically want to know the distance to ground while flying, so when you get too close I can switch animation into landing pose, shortly before you actually land. And I want it to be a little easier to stay afloat when close to the ground, or to even gain some height while gliding above an elevating ground, also at cliffs and the like. In a way close/similar to how it is in this video: https://youtu.be/NOyGBUxEaPY But with the controls and maneuverability from/like in this (current state of my scripts, recorded yesterday): Link to comment Share on other sites More sharing options...
Recommended Posts