View Full Version : can i move a non-bot along pathnodes
legacy-fassbinder
03-12-2006, 06:21 PM
hi
i'm trying to get an actor to follow any bot that bumps into it. i've got it working just using positional data and a little vector math but i'd really like to get it following the level's pathnodes towards its target ... make it look more intelligent. the actor isn't AIcontrolled though so i can't use the controllers:MoveToward(). if i can grab the position from the next pathnode toward the bot target then i can fall back on using vector math and actor:Move() but i'm unsure how to get position from a navigationPoint ... looks like they're held in its PathList array of ReachSpecs which in turn have start and end pathnodes ... but i'm back to same porblem ... where's the location vector from each pathnode? i've include a little code which might explain a little better.
cheers for your help :)
if(Pawn(aTarget) != none)
{
bestPath = Pawn(aTarget).Controller.FindPathToward(Pawn(aTarg et));
if(PathNode(bestPath) != none)
{
if(PathNode(bestPath).PathList[0].start != none)
{
// use start and end pathnodes to determine next direction
// then call actor::Move(vector direction)
}
}
}
legacy-pcmods
03-13-2006, 01:25 AM
You may be looking at re-writing some code here, but path navigating is native to the game,, you could extend your object from the controller class, then you could utilize the navigation commands in the engine. Controller class has very little AI and needs to be programmed to do what you want.
legacy-AquaticSheep
03-13-2006, 04:20 AM
var NavigationPoint Anchor; // current nearest path;
var const NavigationPoint LastAnchor; // recent nearest path
why not add the .location of these to some of your vector math?
or give the actor a timer where it takes the location of the owner every second and interpolated towards that? This post has been brought to you by the tired unhelpful baz association
legacy-^::B!G-A::
03-13-2006, 06:38 PM
AquaticSheep the only problem with that is u need to calculate the path somehow, the interpolation is the easy part
legacy-fassbinder
03-13-2006, 09:53 PM
thanks for all the feedback
i've been trying a few of the suggestions but not having any luck. changed my class to extend controller so i can access the moveTo functions but i think there might an issue with the fact there's no pawn attached to the controller. when i try:
local actor BestPath;
if(Pawn(aTarget) != none)
{
BestPath = FindPathTo(aTarget.Location);
// then access BestPath.Location etc.
}
which i assume returns the next pathnode, I'm getting the following log warning: DevPath: Warning: No pawn for FindPath by myClass in State myClass.myClass.follow ... and obviously no assignment to BestPath. The aTarget has already been set during a Touch() so i guess it's to do with this.controller.
guess it's a bit trickier than i anticipated ;)
cheers
Perhaps a controller requires a Pawn to function properly.
Most FindPath functions return an Actor? :confused: What is one to do with these actors? Are they pathnodes? Game objectives? Pawns?
Reason I'm asking is that I would like to code smart missiles that follow pathnodes...
legacy-^::B!G-A::
03-15-2006, 10:25 PM
In the past two days i have constructed a RocketAI in its begginings:
Controller:
class ProjectileController extends Controller;
var Actor Seeking;
var ControlledRocket ControlledRocket;
simulated function Tick(float DeltaTime)
{
Super.Tick(DeltaTime);
if( ControlledRocket.Seeking != None && Seeking == None )
Seeking = ControlledRocket.Seeking;
ControlledRocket.NextSeekingPath = FindPathToward(Seeking);
}
defaultproperties
{
bGodMode=True
}
The Rocket:
// Summon Test.ControlledRocket
class ControlledRocket extends Pawn;
var float Speed, Damage, DamageRadius, MomentumTransfer, CollisionCheckDist;
var class<DamageType> MyDamageType;
var Actor Seeking;
var Actor NextSeekingPath;
var vector Dir, InitialDir;
var bool bHi****er;
var xEmitter SmokeTrail;
var Effects Corona;
replication
{
reliable if( bNetInitial && (Role==ROLE_Authority) )
Seeking, NextSeekingPath, InitialDir;
}
simulated function Destroyed()
{
if ( SmokeTrail != None )
SmokeTrail.mRegen = False;
if ( Corona != None )
Corona.Destroy();
if( Controller != None )
Controller.Destroy();
Super.Destroyed();
}
simulated function PostBeginPlay()
{
local Vehicle A;
Super.PostBeginPlay();
// For Testing Purposes //
ForEach DynamicActors(Class'Vehicle',A)
if( A != None )
Seeking = A;
//////////////////////////
if ( Level.NetMode != NM_DedicatedServer)
{
//SmokeTrail = Spawn(class'RocketTrailSmoke',self);
SmokeTrail = Spawn(Class'TransTrail',self);
SmokeTrail.mLifeRange[0]=3;
SmokeTrail.mLifeRange[1]=3;
SmokeTrail.mGrowthRate=1.00;
Corona = Spawn(class'RocketCorona',self);
}
Dir = vector(Rotation);
Velocity = Speed * Dir;
Acceleration = Velocity;
if (PhysicsVolume.bWaterVolume)
{
bHi****er = True;
Velocity=0.6*Velocity;
}
Controller = Spawn(Class'ProjectileController');
Controller.Possess(self);
ProjectileController(Controller).ControlledRocket = Self;
SetTimer(0.1, true);
}
simulated function PostNetBeginPlay()
{
local RocketProj R;
local int i;
local PlayerController PC;
Super.PostNetBeginPlay();
if ( Level.NetMode == NM_DedicatedServer )
return;
if ( Level.bDropDetail || (Level.DetailMode == DM_Low) )
{
bDynamicLight = false;
LightType = LT_None;
}
else
{
PC = Level.GetLocalPlayerController();
if ( (Instigator != None) && (PC == Instigator.Controller) )
return;
if ( (PC == None) || (PC.ViewTarget == None) || (VSize(PC.ViewTarget.Location - Location) > 3000) )
{
bDynamicLight = false;
LightType = LT_None;
}
}
}
simulated function Landed( vector HitNormal )
{
Explode(Location,HitNormal);
}
simulated function HitWall(vector HitNormal, actor Wall)
{
Explode(Location, HitNormal );
}
function UnPossessed()
{
Explode(Location, vector(rotation)*-1 );
}
/*
simulated singular function Touch(Actor Other)
{
Explode(Location, vector(rotation)*-1 );
}
*/
simulated singular function Bump(Actor Other)
{
Explode(Location, vector(rotation)*-1 );
}
function BlowUp(vector HitLocation)
{
HurtRadius(Damage, DamageRadius, MyDamageType, MomentumTransfer, HitLocation );
MakeNoise(1.0);
}
simulated function Explode(vector HitLocation, vector HitNormal)
{
local PlayerController PC;
PlaySound(sound'WeaponSounds.BExplosion3',,2.5*Tra nsientSoundVolume);
if ( EffectIsRelevant(Location,false) )
{
Spawn(class'NewExplosionA',,,HitLocation + HitNormal*20,rotator(HitNormal));
PC = Level.GetLocalPlayerController();
if ( (PC.ViewTarget != None) && VSize(PC.ViewTarget.Location - Location) < 5000 )
Spawn(class'ExplosionCrap',,, HitLocation + HitNormal*20, rotator(HitNormal));
// if ( (ExplosionDecal != None) && (Level.NetMode != NM_DedicatedServer) )
// Spawn(ExplosionDecal,self,,Location, rotator(-HitNormal));
}
BlowUp(HitLocation);
Destroy();
}
simulated function Tick(float DeltaTime)
{
local vector ForceDir, AddForce;
local float VelMag;
if ( InitialDir == vect(0,0,0) )
InitialDir = Normal(Velocity);
if ( (NextSeekingPath != None) && (NextSeekingPath != Instigator) )
{
if( NextSeekingPath != Seeking )
AddForce = Vect(0,0,50);
// Do normal guidance to target.
ForceDir = Normal((NextSeekingPath.Location+AddForce) - Location);
if( (ForceDir Dot InitialDir) > 0 )
{
VelMag = VSize(Velocity);
// track vehicles better
if ( NextSeekingPath.Physics == PHYS_Karma )
ForceDir = Normal(ForceDir * 0.80 * VelMag + Velocity);
else
ForceDir = Normal(ForceDir * 0.80 * VelMag + Velocity);
Velocity = (VelMag * ForceDir) + CheckCollision();
Acceleration = Velocity;
}
// Update rocket so it faces in the direction its going.
SetRotation(rotator(Velocity));
}
else if ( NextSeekingPath == None )
log("No target!");
}
simulated function vector CheckCollision()
{
local vector AddForce;
if( !FastTrace(Location+CollisionCheckDist*vect(0,0,1) ) )
AddForce += vect(0,0,-1)*Speed*0.6;
if( !FastTrace(Location+CollisionCheckDist*vect(0,0,-1)) )
AddForce += vect(0,0,1)*Speed*0.6;
if( !FastTrace(Location+CollisionCheckDist*vect(0,1,0) ) )
AddForce += vect(0,-1,0)*Speed*0.6;
if( !FastTrace(Location+CollisionCheckDist*vect(0,-1,0)) )
AddForce += vect(0,1,0)*Speed*0.6;
if( !FastTrace(Location+CollisionCheckDist*vect(1,0,0) ) )
AddForce += vect(-1,0,0)*Speed*0.6;
if( !FastTrace(Location+CollisionCheckDist*vect(-1,0,0)) )
AddForce += vect(1,0,0)*Speed*0.6;
return AddForce;
}
defaultproperties
{
CollisionCheckDist=32.00
MyDamageType=Class'DamTypeRocketHoming'
LifeSpan=30.00
Speed=600.00
AirSpeed=600.00
Damage=90.00
DamageRadius=220.00
MomentumTransfer=50000.00
bSimulateGravity=False
bDirectHitWall=True
bHideRegularHUD=True
bSpecialHUD=True
bNoTeamBeacon=True
bCanUse=False
BaseEyeHeight=0.00
EyeHeight=0.00
LandMovementState=PlayerRocketing
LightType=LT_Steady
LightEffect=LE_QuadraticNonIncidence
LightHue=28
LightBrightness=255.00
LightRadius=5.00
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'WeaponStaticMesh.RocketProj'
bDynamicLight=True
bStasis=False
bReplicateInstigator=True
bNetInitialRotation=True
Physics=PHYS_Flying
NetPriority=3.00
AmbientSound=Sound'WeaponSounds.RocketLauncher.Roc ketLauncherProjectile'
DrawScale=1.00
AmbientGlow=96
bGameRelevant=True
bCanTeleport=False
SoundRadius=100.00
TransientSoundVolume=1.00
TransientSoundRadius=5000.00
CollisionRadius=5.00
CollisionHeight=5.00
bBlockActors=False
ForceType=FT_Constant
ForceRadius=100.00
ForceScale=5.00
}
If this code could be made more efficiant and reposted, that would be great :D (PS: If this code structure is used plz throw me in the credits if u'd like ^_^)
Cool! I'll have a look at it right away. :)
EDIT: Whoa, this is fun! :D Only it doesn't seem to realize it can fly. Crashes into terrain a lot. Loads of potential, though. I'll see what I can do.
Right... tough cookie.
I refactored the code and messed around with it. I haven't managed to improved its workings, but the refactoring should make that slightly easier.
Please have a look at it and see if you can get it to follow the damn pathnodes.
I wish there was a way to display the pathnodes... That's the worst problem: I can't see where the rocket is trying to go.
I can't get it to collide with projectiles either.
Rocket
class ControlledRocket extends Pawn;
var(Damage) float Damage;
var(Damage) float DamageRadius;
var(Damage) float MomentumTransfer;
var(Damage) class<DamageType> MyDamageType;
var(Seek) Actor Seeking;
var(Seek) Actor NextSeekingPath;
var(Seek) float TurnStrength;
var(Seek) float DesiredAltitude;
var(Seek) float WallAvoidance;
var(Seek) float WallCheckDistance;
var(Seek) vector Directions[6];
var(Movement) range Speed;
var(Movement) vector Dir;
var(Movement) vector InitialDir;
var(Trail) xEmitter SmokeTrail;
var(Trail) Effects Corona;
var(Explosion) class<Projector> ExplosionDecal;
var(Explosion) class<Emitter> ExplosionEmitter;
replication
{
reliable if(bNetInitial && Role==ROLE_Authority)
Seeking, NextSeekingPath, InitialDir;
}
simulated event Destroyed()
{
DestroyTrail();
DestroyController();
super.Destroyed();
}
simulated event PostBeginPlay()
{
super.PostBeginPlay();
GetTestTarget();
SpawnTrail();
CheckLighting();
SetInitialVelocity();
SpawnController();
}
simulated event Landed(vector HitNormal)
{
Explode(Location,HitNormal);
}
simulated event HitWall(vector HitNormal, actor Wall)
{
Explode(Location, HitNormal);
}
event UnPossessed()
{
Explode(Location, vector(rotation)*-1);
}
/*
simulated singular event Touch(Actor Other)
{
Explode(Location, vector(rotation)*-1);
}
*/
simulated singular event Bump(Actor Other)
{
Explode(Location, vector(rotation)*-1);
}
simulated event Tick(float DeltaTime)
{
Seek(DeltaTime);
}
/** Movement: Start moving. */
function SetInitialVelocity()
{
Dir = vector(Rotation);
Velocity = Speed.Min * Dir;
Acceleration = Velocity;
InitialDir = Velocity;
if(PhysicsVolume.bWaterVolume)
{
Velocity *= 0.6;
}
}
/** Controller: Set up controller. */
function SpawnController()
{
Controller = Spawn(class'ProjectileController');
Controller.Possess(self);
}
/** Controller: Clean up controller. */
function DestroyController()
{
if(Controller != none)
{
Controller.Destroy();
}
}
/** Trail: Set up smoke and corona. */
simulated function SpawnTrail()
{
if(Level.NetMode != NM_DedicatedServer)
{
SmokeTrail = Spawn(class'TransTrail', self);
SmokeTrail.mLifeRange[0] = 3;
SmokeTrail.mLifeRange[1] = 3;
SmokeTrail.mGrowthRate = 1.00;
Corona = Spawn(class'RocketCorona', self);
}
}
/** Trail: Clean up smoke and corona. */
simulated function DestroyTrail()
{
if(SmokeTrail != none)
{
SmokeTrail.mRegen = false;
}
if(Corona != none)
{
Corona.Destroy();
}
}
/** Lighting: See if lighting is required. */
function CheckLighting()
{
local PlayerController PC;
if(Level.bDropDetail
|| Level.DetailMode == DM_Low)
{
TurnOffLight();
}
else
{
PC = Level.GetLocalPlayerController();
if(Instigator == none
|| PC == Instigator.Controller)
{
if(PC == none
|| PC.ViewTarget == none
|| VSize(PC.ViewTarget.Location - Location) > 3000)
{
TurnOffLight();
}
}
}
}
/** Lighting: Turn off lighting. */
simulated function TurnOffLight()
{
bDynamicLight = false;
LightType = LT_None;
}
/** Test: Find a vehicle to chase. */
function GetTestTarget()
{
local Vehicle A;
foreach DynamicActors(class'Vehicle', A)
{
Seeking = A;
//return;
}
}
/** Seek: Seek the target. */
function Seek(float DeltaTime)
{
local vector SeekLocation;
local vector SeekDirection;
if(NextSeekingPath != none
&& NextSeekingPath != Instigator)
{
SeekLocation = GetSeekLocation();
SeekDirection = Normal(SeekLocation - Location);
MoveIn(SeekDirection, DeltaTime);
// Orient to velocity.
SetRotation(rotator(Velocity));
}
else if(NextSeekingPath == none) Level.Game.Broadcast(Instigator.Controller, "No target!", 'Say');
}
/** Seek: Figure out where to go. */
simulated function vector GetSeekLocation()
{
local vector SeekLocation;
SeekLocation = NextSeekingPath.Location;
if(NextSeekingPath != Seeking)
{
// Keep away from the ground.
SeekLocation += vect(0,0,1) * DesiredAltitude;
}
// Keep away from obstacles.
SeekLocation += CheckCollision();
return SeekLocation;
}
/** Seek: Avoid collisions. */
simulated function vector CheckCollision()
{
local vector AddForce;
local int i;
// Check ahead first.
if(!FastTrace(Location + Normal(Velocity) * WallCheckDistance))
{
// Move up a bit more.
AddForce += AvoidWall(vect(0,0,-1));
// Move clear of obstacles.
for(i = 0; i < arraycount(Directions); i++)
{
AddForce += AvoidWall(Directions[i]);
}
}
return AddForce;
}
/** Seek: Steer clear of obstacle in WallDirection. */
simulated function vector AvoidWall(vector WallDirection)
{
local vector AddForce;
if(CloseToWall(WallDirection))
{
AddForce -= WallDirection * WallAvoidance;
}
return AddForce;
}
/** Seek: See if there's an obstacle in WallDirection. */
simulated function bool CloseToWall(vector WallDirection)
{
return(!FastTrace(Location + WallCheckDistance * WallDirection));
}
/** Seek: Move in on the target. Standard vector homing. */
function MoveIn(vector SeekingDirection, float DeltaTime)
{
local vector HomingForce;
local vector NewDirection;
HomingForce = SeekingDirection * TurnStrength * VSize(Velocity) * DeltaTime;
NewDirection = Normal(Velocity + HomingForce);
Velocity = NewDirection * FClamp(VSize(Velocity), Speed.Min, Speed.Max);
}
/** Explosion: BOOM! */
simulated function Explode(vector HitLocation, vector HitNormal)
{
SpawnExplosion(HitLocation, HitNormal);
HurtRadius(Damage, DamageRadius, MyDamageType, MomentumTransfer, HitLocation);
MakeNoise(1.0);
Destroy();
}
/** Explosion: Spawn effects. */
function SpawnExplosion(vector HitLocation, vector HitNormal)
{
PlaySound(sound'WeaponSounds.BExplosion3',, TransientSoundVolume);
if(EffectIsRelevant(Location,false))
{
Spawn(ExplosionEmitter,,, HitLocation + HitNormal * 20,rotator(HitNormal));
SpawnScorchMark(HitLocation, HitNormal);
}
}
/** Explosion: Spawn scorch mark. */
function SpawnScorchMark(vector HitLocation, vector HitNormal)
{
local PlayerController PC;
PC = Level.GetLocalPlayerController();
if(ExplosionDecal != none
&& Level.NetMode != NM_DedicatedServer
&& PC.ViewTarget != none
&& VSize(PC.ViewTarget.Location - Location) < 5000)
{
Spawn(ExplosionDecal,self,, Location, rotator(-HitNormal));
}
}
defaultproperties
{
DrawType=DT_StaticMesh
StaticMesh=StaticMesh'WeaponStaticMesh.RocketProj'
DrawScale=1.0
bReplicateInstigator=True
bNetInitialRotation=True
NetPriority=3.0
bGameRelevant=True
LifeSpan=30.0
Physics=PHYS_Flying
bSimulateGravity=False
bStasis=False
LandMovementState=PlayerRocketing
bCanTeleport=False
bCanFly=true
bCanUse=False
Speed=(Min=100,Max=1000)
TurnStrength=0.80
DesiredAltitude=50
WallAvoidance=0.6
WallCheckDistance=2000
Directions(0)=(X=-1)
Directions(1)=(X=+1)
Directions(2)=(Y=-1)
Directions(3)=(Y=+1)
Directions(4)=(Z=-1)
Directions(5)=(Z=+1)
CollisionRadius=5.0
CollisionHeight=5.0
bBlockActors=False
bDirectHitWall=True
bHideRegularHUD=True
bSpecialHUD=True
bNoTeamBeacon=True
BaseEyeHeight=0.0
EyeHeight=0.0
LightType=LT_Steady
LightEffect=LE_QuadraticNonIncidence
LightHue=28
LightBrightness=255.0
LightRadius=5.0
bDynamicLight=True
AmbientGlow=96
SoundRadius=100.00
SoundVolume=255.0
TransientSoundVolume=1.00
TransientSoundRadius=5000.0
AmbientSound=Sound'WeaponSounds.RocketLauncher.Roc ketLauncherProjectile'
ExplosionDecal=class'ONSRocketScorch'
ExplosionEmitter=class'UT2k4Assault.FX_SpaceFighte r_Explosion'
Damage=1000.0
DamageRadius=220.0
MomentumTransfer=50000.0
MyDamageType=class'DamTypeRocketHoming'
}
Controller
class ProjectileController extends Controller;
var(Seek) Actor Seeking;
var(Rocket) ControlledRocket Rocket;
var Actor LastSeeking;
simulated function Tick(float DeltaTime)
{
if(Rocket != none)
{
if(Rocket.Seeking != none
&& Seeking == none)
{
Seeking = Rocket.Seeking;
}
Rocket.NextSeekingPath = FindPathToward(Seeking);
if(Rocket.NextSeekingPath != LastSeeking) Level.Game.Broadcast(Level.GetLocalPlayerControlle r(), "Seeking " $ Rocket.NextSeekingPath, 'Say'); LastSeeking = Rocket.NextSeekingPath;
}
else
{
Destroy();
}
}
simulated function Possess(Pawn P)
{
if(ControlledRocket(P) != none)
{
super.Possess(P);
Rocket = ControlledRocket(P);
}
}
defaultproperties
{
bGodMode=True
}
legacy-fassbinder
03-16-2006, 06:41 PM
wow, i've been away from the site the last few days ... looks like you guys have been running with this ;) i'll try and get my head round what you're up to although i'm still relatively new to using US.
i was wondering, have you thought about how the WillowWhisp object moves around ... i had a quick look and it seems to be copying out all the location points from an 'owner's RouteCache into a vector array called WayPoints then moving along those points using vector calculations. i might be barking up the wrong tree here but it looks like it might be useful.
cheers fassbinder
legacy-fassbinder
03-16-2006, 08:35 PM
hey Xyx
there's a simple way to highlight limited numbers of pathnode using the Actor::drawdebug commands. I included the following for-loop in the Tick() of my own bots and it seems to work fine, highlighting each bot's following 3 pathnodes in their RouteCache (i tried drawing out all the pathnodes but managed to crash UT... probably bad coding on my part) Anyway, both Sphere and Line functions work. DrawDebugCircle() will likely work too. I realise this isn't drawing out all the pathnodes but i'm thinking you could just try output pathnodes from the 'Seeking' actor in the ProjectileController class???
function Tick( float DeltaTime )
{
local int i;
for(i = 0; i < 3; i++)
{
if(RouteCache[i] != none && RouteCache[i+1] != none)
//DrawDebugSphere(RouteCache[i].Location, 50.f, 16, 255, 0, 0);
DrawDebugLine(RouteCache[i].Location, RouteCache[i+1].Location, 255,0,0);
}
}
hope that's of some use.
cheers
fassbinder
legacy-^::B!G-A::
03-16-2006, 10:19 PM
[EDIT] These organized classes compare nothing to the junk i make, great job putting them back together
And, the TransTrail was just a crude way to show its path :P
Im going to add the debug drawing in and see wat i can change to fix it up a bit or not
Powered by vBulletin® Version 4.2.0 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.