public override void Fire() { // Play 'pew' sound Actor bolt = GameResources.ActorManager.SpawnTemplate(ProjectileTemplateName); Actor owner = GameResources.ActorManager.GetActorById(OwnerActorId); BipedControllerComponent bipedControl = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); TransformComponent boltXform = bolt.GetComponent <TransformComponent>(ActorComponent.ComponentType.Transform); Vector3 aim = (bipedControl.WorldAim.HasValue ? bipedControl.WorldAim.Value : BepuConverter.Convert(bipedControl.Controller.ViewDirection)); boltXform.Transform = Matrix.CreateTranslation(MuzzleOffset) * Matrix.CreateWorld(BepuConverter.Convert( bipedControl.Controller.Body.Position), aim, Vector3.Up); EnergyProjectile boltProj = bolt.GetBehavior <EnergyProjectile>(); boltProj.Propel(OwnerActorId); }
private void SetHorizontalMovementAloof() { // Map raw movement vector into our 2D movement space: if (HorizontalMovement.Y > 0.0f) // Positive Y is special because we can move faster in that direction. { double rawMovementTheta = Math.Atan2(HorizontalMovement.Y, HorizontalMovement.X); float maxRadius = (float)(GetMaxFwdMoveLength(rawMovementTheta)); float totalMovementLength = HorizontalMovement.Length() * maxRadius; // Clamp the movement: Controller.HorizontalMotionConstraint.SpeedScale = 0.5f * Math.Min(maxRadius, totalMovementLength); } else { // Clamp the movement: Controller.HorizontalMotionConstraint.SpeedScale = 0.5f * Math.Min(1.0f, HorizontalMovement.Length()); } Controller.HorizontalMotionConstraint.MovementDirection = BepuConverter.Convert(HorizontalMovement); }
protected override void ComponentsCreatedHandler(object sender, EventArgs e) { BipedControllerComponent bipedController = Owner.GetComponent <BipedControllerComponent>(ComponentType.Control); if (bipedController == null) { throw new LevelManifestException("BipedCollisionComponents expect to be accompanied by BipedControllerComponents."); } mSimController = bipedController.Controller; mSimController.StanceManager.StandingHeight = mHeight; mSimController.StanceManager.CrouchingHeight = mHeight * 0.5f; mSimController.BodyRadius = mRadius; mSimController.Body.Mass = mMass; base.ComponentsCreatedHandler(sender, e); mSimController.ViewDirection = BepuConverter.Convert(Vector3.Transform(Vector3.Forward, mTransformComponent.Orientation)); }
// Get to a target ASAP. private Vector2 Seek(Actor owner) { BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); Vector3 relativeTarget = Target - BepuConverter.Convert(bcc.Controller.Body.Position); if (relativeTarget.X == 0.0f && relativeTarget.Z == 0.0f) { return(Vector2.Zero); } // Rotate into controller space. Matrix controllerRotation = Matrix.CreateLookAt(Vector3.Zero, BepuConverter.Convert(bcc.Controller.HorizontalViewDirection), Vector3.Up); relativeTarget = Vector3.Transform(relativeTarget, controllerRotation); // Project the target onto the horizontal plane and normalize (via simple trig since we know Y == 0.0f). // Take theta = 0.0f is facing forward (-Z) double theta = Math.Atan2(-relativeTarget.X, -relativeTarget.Z); return(new Vector2(-(float)(Math.Sin(theta)), (float)(Math.Cos(theta)))); }
// For non-vision memory input. public void SenseFoe(int actorId) { AggroRecord aggro; if (!mFoes.ContainsKey(actorId)) { aggro = new AggroRecord(); aggro.Enmity = AggroRecord.NEW_FOE_ENMITY; mFoes.Add(actorId, aggro); } else { aggro = mFoes[actorId]; } Actor foe = GameResources.ActorManager.GetActorById(actorId); BipedControllerComponent bcc = foe.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); aggro.PositionLastSensed = BepuConverter.Convert(bcc.Controller.Body.Position); aggro.TimeLastSensed = GameResources.ActorManager.CurrentTime; }
private void CommitOrientationAndMovement() { if (mState == ControllerState.InputDisabled) { Controller.HorizontalMotionConstraint.SpeedScale = 0.0f; Controller.HorizontalMotionConstraint.MovementDirection = BEPUutilities.Vector2.UnitY; return; } float diffAngle = (float)(SpaceUtils.GetQuaternionAngle(OrientationChange)); if (diffAngle > MaxTurnAnglePerTick) { OrientationChange = Quaternion.Slerp(Quaternion.Identity, OrientationChange, MaxTurnAnglePerTick / diffAngle); HorizontalMovement = Vector2.Zero; // We spent our movement turning (but this won't affect boosting.) } Controller.ViewDirection = BEPUutilities.Quaternion.Transform(Controller.ViewDirection, BepuConverter.Convert(OrientationChange)); OrientationChange = Quaternion.Identity; if (mState == ControllerState.Boosting) { Controller.HorizontalMotionConstraint.SpeedScale = 1.0f; Controller.HorizontalMotionConstraint.MovementDirection = BEPUutilities.Vector2.UnitY; } else { if (AimCheck()) { Controller.HorizontalMotionConstraint.SpeedScale = 0.375f * Math.Min(1.0f, HorizontalMovement.Length()); Controller.HorizontalMotionConstraint.MovementDirection = BepuConverter.Convert(HorizontalMovement); } else { SetHorizontalMovementAloof(); } } }
public override void Fire() { // Play 'pop' sound Actor owner = GameResources.ActorManager.GetActorById(OwnerActorId); WeaponResource wr = owner.GetBehavior <WeaponResource>(); wr.Value -= ResourceCostToUse; BipedControllerComponent bipedControl = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); Vector3 aim = (bipedControl.WorldAim.HasValue ? bipedControl.WorldAim.Value : BepuConverter.Convert(bipedControl.Controller.ViewDirection)); Matrix muzzleTransform = Matrix.CreateTranslation(MuzzleOffset) * Matrix.CreateWorld(BepuConverter.Convert( bipedControl.Controller.Body.Position), aim, Vector3.Up); BepuRay shootRay = new BepuRay(BepuConverter.Convert(muzzleTransform.Translation), BepuConverter.Convert(muzzleTransform.Forward)); RayCastResult result; GameResources.ActorManager.SimSpace.RayCast(shootRay, 500.0f, out result); EntityCollidable otherEntityCollidable = result.HitObject as EntityCollidable; Terrain otherTerrain = result.HitObject as Terrain; if (otherEntityCollidable != null && otherEntityCollidable.Entity != null && otherEntityCollidable.Entity.Tag != null) { Actor actorHit = GameResources.ActorManager.GetActorById((int)(otherEntityCollidable.Entity.Tag)); IDamagable damage = actorHit.GetBehaviorThatImplementsType <IDamagable>(); if (damage != null) { damage.TakeDamage(Damage); // TODO: P2: Query hit actor for appropiate damage effect e.g. blood and create it; } HitSparks(result.HitData.Location); } else if (otherTerrain != null) { HitSparks(result.HitData.Location); } }
public override void Fire() { const float ATTACK_RADIUS = 3.0f; const float ATTACK_LENGTH = 4.0f; // Play 'thwack' sound Actor owner = GameResources.ActorManager.GetActorById(OwnerActorId); BipedControllerComponent bipedControl = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); RigidTransform alignCapsule = new RigidTransform(BepuVec3.Forward * ATTACK_LENGTH * 0.5f + BepuConverter.Convert(MuzzleOffset), BepuQuaternion.CreateFromAxisAngle(BepuVec3.Right, MathHelper.PiOver2)); Vector3 aim = (bipedControl.WorldAim.HasValue ? bipedControl.WorldAim.Value : BepuConverter.Convert(bipedControl.Controller.ViewDirection)); RigidTransform positionAndAim = new RigidTransform(bipedControl.Controller.Body.Position, BepuConverter.Convert( SpaceUtils.GetOrientation(aim, Vector3.Up))); RigidTransform attackTransform; RigidTransform.Transform(ref alignCapsule, ref positionAndAim, out attackTransform); ConvexShape bashShape = new CapsuleShape(ATTACK_LENGTH, ATTACK_RADIUS); BepuVec3 noSweep = BepuVec3.Zero; List <RayCastResult> dudesBashed = new List <RayCastResult>(); AttackFilter filter = new AttackFilter(GameResources.ActorManager.IsMob(OwnerActorId)); GameResources.ActorManager.SimSpace.ConvexCast(bashShape, ref attackTransform, ref noSweep, filter.Test, dudesBashed); foreach (RayCastResult dude in dudesBashed) { EntityCollidable otherEntityCollidable = dude.HitObject as EntityCollidable; Terrain otherTerrain = dude.HitObject as Terrain; if (otherEntityCollidable != null && otherEntityCollidable.Entity != null && otherEntityCollidable.Entity.Tag != null) { Actor actorHit = GameResources.ActorManager.GetActorById((int)(otherEntityCollidable.Entity.Tag)); IDamagable damage = actorHit.GetBehaviorThatImplementsType <IDamagable>(); if (damage != null) { damage.TakeDamage(Damage); // TODO: P2: Query hit actor for appropiate damage effect e.g. blood and create it; } BashDust(dude.HitData.Location); } else if (otherTerrain != null) { BashDust(dude.HitData.Location); } } }
private void PreAnimationUpdateHandler(object sender, UpdateStepEventArgs e) { float elapsedTime = (float)(e.GameTime.ElapsedGameTime.TotalSeconds); Actor avatar = GameResources.ActorManager.GetActorById(ActorId); BipedControllerComponent bipedControl = avatar.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); // Update the camera. Vector3 desiredCameraPosition; if (mInputMode == InputMode.Aloof) { Matrix cameraRotation = Matrix.CreateFromYawPitchRoll(mMmoCameraDesc.Yaw, mMmoCameraDesc.Pitch, 0.0f); BepuRay boomRay = new BepuRay(mAvatarBepuEntity.Position, BepuConverter.Convert(cameraRotation.Backward)); RayCastResult result; GameResources.ActorManager.SimSpace.RayCast(boomRay, mMmoCameraDesc.Distance, CameraClipFilter, out result); desiredCameraPosition = result.HitObject != null? BepuConverter.Convert(BEPUutilities.Vector3.Lerp(result.HitData.Location, mAvatarBepuEntity.Position, 0.05f)) : BepuConverter.Convert(mAvatarBepuEntity.Position) + mMmoCameraDesc.Distance * cameraRotation.Backward; } else if (mInputMode == InputMode.Aiming) { Matrix viewRotation = Matrix.CreateWorld(Vector3.Zero, BepuConverter.Convert( bipedControl.Controller.ViewDirection), Vector3.Up); desiredCameraPosition = BepuConverter.Convert(mAvatarBepuEntity.Position) + Vector3.Transform( mAimingCameraOffset, viewRotation); } else { desiredCameraPosition = mCamera.Transform.Translation; } Vector3 newCameraPosition = desiredCameraPosition; Vector3 desiredCameraDirection; if (mInputMode == InputMode.Aloof) { desiredCameraDirection = BepuConverter.Convert(mAvatarBepuEntity.Position) - newCameraPosition; } else if (mInputMode == InputMode.Aiming) { desiredCameraDirection = BepuConverter.Convert(bipedControl.Controller.ViewDirection); } else { desiredCameraDirection = mCamera.Transform.Forward; } desiredCameraDirection.Normalize(); Vector3 newCameraDirection = desiredCameraDirection; if (mCameraSmoothingEngaged) { Vector3 positionDelta = desiredCameraPosition - mCamera.Transform.Translation; Quaternion directionDelta = SpaceUtils.GetSweptQuaternion(mCamera.Transform.Forward, desiredCameraDirection); const float POSITION_DELTA_THRESHHOLD = 4.0f; const float DIRECTION_DELTA_THRESHHOLD = MathHelper.Pi / 16.0f; float positionDeltaLength = positionDelta.Length(); float directionDeltaAngle = (float)(SpaceUtils.GetQuaternionAngle(directionDelta)); float fractionComplete = Math.Min(POSITION_DELTA_THRESHHOLD / positionDeltaLength, DIRECTION_DELTA_THRESHHOLD / directionDeltaAngle); if (fractionComplete < 1.0f) { newCameraPosition = Vector3.Lerp(mCamera.Transform.Translation, desiredCameraPosition, fractionComplete); Quaternion smoothedCamRotation = Quaternion.Slerp(Quaternion.Identity, directionDelta, fractionComplete); newCameraDirection = Vector3.Transform(mCamera.Transform.Forward, smoothedCamRotation); } } else { mCameraSmoothingEngaged = true; } mCamera.Transform = Matrix.CreateWorld(newCameraPosition, newCameraDirection, Vector3.Up); }
public override void Update(/* inout */ SteeringBlender steering, Actor owner, IAgentStateManager agent) { if (!agent.HasProperty(AgentPropertyName.ActiveOpponent)) { ZombieWaitState zws = new ZombieWaitState(6.0f); agent.CurrentState = zws; return; } Actor opponent = GameResources.ActorManager.GetActorById(agent.GetProperty <int>(AgentPropertyName.ActiveOpponent)); BipedControllerComponent opponentBcc = opponent.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); steering.Target = BepuConverter.Convert(opponentBcc.Controller.Body.Position); BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); BepuVec3 toOpponent = opponentBcc.Controller.Body.Position - bcc.Controller.Body.Position; float distance = toOpponent.Length(); ZombieSkillSet zss = owner.GetBehaviorThatImplementsType <ZombieSkillSet>(); BipedWeapon chosenAttack = distance <= zss.MeleeSkill.EffectiveRangeMax ? zss.MeleeSkill : zss.RangedSkill; Matrix attackTransform = Matrix.CreateTranslation(chosenAttack.MuzzleOffset) * Matrix.CreateWorld( BepuConverter.Convert(bcc.Controller.Body.Position), BepuConverter.Convert(bcc.Controller.ViewDirection), Vector3.Up); BepuVec3 bulletPath = opponentBcc.Controller.Body.Position - BepuConverter.Convert(attackTransform.Translation); // If we don't have a shot, we need to specify what kind of movement we need to remedy that. ZombieTacticalMovementState.MovementType movement = ZombieTacticalMovementState.MovementType.None; if (distance < chosenAttack.EffectiveRangeMin) { movement = ZombieTacticalMovementState.MovementType.Retreat; } else if (distance > chosenAttack.EffectiveRangeMax) { movement = ZombieTacticalMovementState.MovementType.Close; } else { BepuRay loeRay = new BepuRay(BepuConverter.Convert(attackTransform.Translation), bulletPath); LOSFilter filter = new LOSFilter(bcc.Controller.Body.CollisionInformation, opponentBcc.Controller.Body.CollisionInformation); RayCastResult loeResult; GameResources.ActorManager.SimSpace.RayCast(loeRay, chosenAttack.EffectiveRangeMax * 1.5f, filter.Test, out loeResult); EntityCollidable otherEntityCollidable = loeResult.HitObject as EntityCollidable; if (otherEntityCollidable != null && otherEntityCollidable.Entity != null && otherEntityCollidable.Entity.Tag != null && (int)(otherEntityCollidable.Entity.Tag) == opponent.Id) { toOpponent.Y = 0.0f; toOpponent.Normalize(); float aimTheta = (float)(Math.Acos(MathHelper.Clamp(BepuVec3.Dot(toOpponent, bcc.Controller.ViewDirection), 0.0f, 1.0f))); const float AIM_CONE_RADIANS = MathHelper.Pi / 12.0f; if (aimTheta <= AIM_CONE_RADIANS) { // TODO: P2: Add some wander to this value: bcc.WorldAim = BepuConverter.Convert(bulletPath); //chosenAttack.CurrentOperation = WeaponFunctions.TriggerPulled; BipedWeapon otherAttack = chosenAttack == zss.MeleeSkill ? zss.RangedSkill : zss.MeleeSkill; otherAttack.UpdateInputState(false, bcc); chosenAttack.UpdateInputState(true, bcc); return; } } else { movement = ZombieTacticalMovementState.MovementType.Lateral; } } if (movement != ZombieTacticalMovementState.MovementType.None) { ZombieTacticalMovementState ztms = new ZombieTacticalMovementState(movement); agent.CurrentState = ztms; } }
private void ProcessAIStepHandler(object sender, UpdateStepEventArgs e) { // Check FOV, add any new foes to memory. And update existing ones. We may also have gained new memories by other means. // Get players and mobs in field of vision: List <RayCastResult> actorsInView = new List <RayCastResult>(); ConeShape visionCone = new ConeShape(VisionDistance, VisionDistance); BipedControllerComponent bcc = Owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); RigidTransform tipOverCone = new RigidTransform(BepuVec3.Forward * VisionDistance * 0.75f, BepuQuaternion.CreateFromAxisAngle(BepuVec3.Right, MathHelper.PiOver2)); RigidTransform eyeLevelAndFacing = new RigidTransform(bcc.Controller.Body.Position - bcc.Controller.Down * bcc.Controller.Body.Height * 0.45f, BepuConverter.Convert(SpaceUtils.GetOrientation(BepuConverter.Convert(bcc.Controller.ViewDirection), Vector3.Up))); RigidTransform visionConeTransform; RigidTransform.Transform(ref tipOverCone, ref eyeLevelAndFacing, out visionConeTransform); BepuVec3 noSweep = BepuVec3.Zero; ViewInterestFilter filter = new ViewInterestFilter(bcc.Controller.Body.CollisionInformation); GameResources.ActorManager.SimSpace.ConvexCast(visionCone, ref visionConeTransform, ref noSweep, filter.Test, actorsInView); for (int a = 0; a < actorsInView.Count; ++a) { // Does this actor warrant an addition to be made to our memory? // If so, check for LOS and recheck range. If those tests pass, modify the memory. EntityCollidable otherEntityCollidable = actorsInView[a].HitObject as EntityCollidable; // We can jump to the Id in the Tag property because we know the filter has validated this. int actorId = (int)(otherEntityCollidable.Entity.Tag); Actor viewedActor = GameResources.ActorManager.GetActorById(actorId); BipedControllerComponent viewedActorBcc = viewedActor.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); BepuVec3 toSubject = viewedActorBcc.Controller.Body.Position - eyeLevelAndFacing.Position; // Check range: if (toSubject.LengthSquared() <= VisionDistance * VisionDistance) { BepuRay losRay = new BepuRay(eyeLevelAndFacing.Position, toSubject); RayCastResult losResult; LOSFilter losFilter = new LOSFilter(bcc.Controller.Body.CollisionInformation, otherEntityCollidable); GameResources.ActorManager.SimSpace.RayCast(losRay, VisionDistance, losFilter.Test, out losResult); EntityCollidable losEC = losResult.HitObject as EntityCollidable; // Test for LOS: if (losEC != null && losEC.Entity != null && losEC.Entity.Tag != null && (int)(losEC.Entity.Tag) == actorId) { // The viewed actor is either a player(foe) or a mob(ally). if (GameResources.ActorManager.IsPlayer(actorId)) { mMemory.SpotFoe(actorId); } else { IAgentStateManager agent = viewedActor.GetBehaviorThatImplementsType <IAgentStateManager>(); if (agent != null && agent.HasProperty(AgentPropertyName.ActiveOpponent)) { int mobFoe = agent.GetProperty <int>(AgentPropertyName.ActiveOpponent); mMemory.SenseFoe(mobFoe); } } } } } // Evaluate current threats and select one to engage: int enemyId = mMemory.GetLargestThreat(); if (enemyId != Actor.INVALID_ACTOR_ID) { if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent)) { if ((int)(mAgentProperties[AgentPropertyName.ActiveOpponent]) != enemyId) { mAgentProperties[AgentPropertyName.ActiveOpponent] = enemyId; } } else { mAgentProperties.Add(AgentPropertyName.ActiveOpponent, enemyId); } } else { if (mAgentProperties.ContainsKey(AgentPropertyName.ActiveOpponent)) { mAgentProperties.Remove(AgentPropertyName.ActiveOpponent); } } TimeInState += e.GameTime.ElapsedGameTime; CurrentState.Update(mSteering, Owner, this); Vector2 locomotion = mSteering.ComputeForce(Owner); if (locomotion.LengthSquared() == 0.0f) { bcc.OrientationChange = Quaternion.Identity; bcc.HorizontalMovement = Vector2.Zero; } else { bcc.OrientationChange = Quaternion.CreateFromAxisAngle(Vector3.Up, (float)(Math.Atan2(-locomotion.X, locomotion.Y))); bcc.HorizontalMovement = locomotion.Length() * Vector2.UnitY; } mMemory.Fade(e.GameTime); }
private void TransformChangedHandler(object sender, EventArgs e) { Entity.Position = BepuConverter.Convert(mTransformComponent.Translation + Vector3.Transform(mTransformOffset, mTransformComponent.Orientation)); Entity.Orientation = BepuConverter.Convert(mTransformComponent.Orientation); }
public void ImpactHandler(object sender, UpdateStepEventArgs e) { GameResources.ActorManager.PostPhysicsUpdateStep -= ImpactHandler; // TODO: P2: Some boooom sound effects... TransformComponent myXForm = Owner.GetComponent <TransformComponent>(ActorComponent.ComponentType.Transform); // Do a sphere cast to get actors in the blast radius List <RayCastResult> inRangeActors = new List <RayCastResult>(); SphereShape blastZone = new SphereShape(mBlastRadius); RigidTransform blastPosition = new RigidTransform(BepuConverter.Convert(myXForm.Translation)); BEPUutilities.Vector3 bepuZero = BEPUutilities.Vector3.Zero; GameResources.ActorManager.SimSpace.ConvexCast(blastZone, ref blastPosition, ref bepuZero, inRangeActors); RayCastDistanceComparer rcdc = new RayCastDistanceComparer(); for (int a = 0; a < inRangeActors.Count; ++a) { EntityCollidable inRangeEntityCollidable = inRangeActors[a].HitObject as EntityCollidable; if (inRangeEntityCollidable != null && inRangeEntityCollidable.Entity != null && inRangeEntityCollidable.Entity.Tag != null) { Actor blastedActor = GameResources.ActorManager.GetActorById((int)(inRangeEntityCollidable.Entity.Tag)); IDamagable actorDamage = blastedActor.GetBehaviorThatImplementsType <IDamagable>(); DynamicCollisionComponent actorCollidable = blastedActor.GetComponent <DynamicCollisionComponent>(ActorComponent.ComponentType.Physics); BepuVec3 blastToActorCenter = actorCollidable.Entity.Position - blastPosition.Position; BepuRay loeRay = new BepuRay(blastPosition.Position, blastToActorCenter); bool hasCover = false; float distance = mBlastRadius; if (actorDamage != null || (actorCollidable != null && actorCollidable.IsDynamic && !(actorCollidable.Entity.CollisionInformation.CollisionRules.Personal.HasFlag(BEPUphysics.CollisionRuleManagement.CollisionRule.NoSolver)))) { List <RayCastResult> loeResults = new List <RayCastResult>(); GameResources.ActorManager.SimSpace.RayCast(loeRay, mBlastRadius, loeResults); loeResults.Sort(rcdc); for (int c = 0; c < loeResults.Count; ++c) { EntityCollidable possibleCover = loeResults[c].HitObject as EntityCollidable; if (possibleCover != null && possibleCover.Entity == inRangeEntityCollidable.Entity) { // Hit distance = loeResults[c].HitData.T; break; } Terrain possibleCoverTerrain = loeResults[c].HitObject as Terrain; if (possibleCoverTerrain != null) { hasCover = true; break; } if (possibleCover != null && possibleCover.Entity != null && !possibleCover.Entity.IsDynamic) { hasCover = true; break; } } } if (!hasCover && actorDamage != null) { actorDamage.TakeDamage((int)(MathHelper.Lerp(1.0f, 0.25f, distance / mBlastRadius) * mDamage)); } if (!hasCover && actorCollidable != null && actorCollidable.IsDynamic && !(actorCollidable.Entity.CollisionInformation.CollisionRules.Personal.HasFlag(BEPUphysics.CollisionRuleManagement.CollisionRule.NoSolver))) { blastToActorCenter.Normalize(); blastToActorCenter = blastToActorCenter * 1200; // Math.Min(5000.0f / (distance + 1.0f)); actorCollidable.Entity.ApplyLinearImpulse(ref blastToActorCenter); if (!actorCollidable.Entity.ActivityInformation.IsActive) { actorCollidable.Entity.ActivityInformation.Activate(); } } } } DynamicCollisionComponent dcc = Owner.GetComponent <DynamicCollisionComponent>(ActorComponent.ComponentType.Physics); Vector3 myVelocity = BepuConverter.Convert(dcc.Entity.LinearVelocity); Actor fireball = GameResources.ActorManager.SpawnTemplate("ExplosionFire"); TransformComponent fireXForm = fireball.GetComponent <TransformComponent>(ActorComponent.ComponentType.Transform); fireXForm.Translation = myXForm.Translation; ExplosionFire fireBehavior = fireball.GetBehavior <ExplosionFire>(); fireBehavior.Emit(myVelocity); Actor smoke = GameResources.ActorManager.SpawnTemplate("ExplosionSmoke"); TransformComponent smokeXForm = smoke.GetComponent <TransformComponent>(ActorComponent.ComponentType.Transform); smokeXForm.Translation = myXForm.Translation; ExplosionSmoke smokeBehavior = smoke.GetBehavior <ExplosionSmoke>(); smokeBehavior.Emit(myVelocity); Owner.Despawn(); }
public Vector2 ComputeForce(Actor owner) { // Aggregate the behaviors to produce an initial 'immediate' result: Vector2 result = Vector2.Zero; if (Weights[(int)WeightType.Seek] > 0.0f) { result += Weights[(int)WeightType.Seek] / TotalWeight * Seek(owner); } if (Weights[(int)WeightType.Arrive] > 0.0f) { result += Weights[(int)WeightType.Arrive] / TotalWeight * Arrive(owner); } if (Weights[(int)WeightType.Wander] > 0.0f) { result += Weights[(int)WeightType.Wander] / TotalWeight * Wander(); } if (Weights[(int)WeightType.Wait] > 0.0f) { result += Weights[(int)WeightType.Wait] / TotalWeight * Wait(); } result = FitIntoSlack(result, AvoidObstacles(owner)); // Create a 3D version of the result (so we can transform it using rotation about the y-axis) Vector3 res3D = new Vector3(result.X, 0.0f, -result.Y); // Keep a history of unit force directions, in world space. And magnitudes. // Rotate into world space before storing so the history is all in the same space. BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); Matrix controllerRotation = Matrix.Transpose(Matrix.CreateLookAt(Vector3.Zero, BepuConverter.Convert(bcc.Controller.HorizontalViewDirection), Vector3.Up)); res3D = Vector3.Transform(res3D, controllerRotation); // Store in the history arrays; mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; mForceHistory[mHistoryIndex] = res3D.Length(); if (mForceHistory[mHistoryIndex] != 0.0f) { mDirHistory[mHistoryIndex] = res3D / mForceHistory[mHistoryIndex]; } else { mDirHistory[mHistoryIndex] = Vector3.Forward; } // Get the average direction: Vector3 dirAvg = Vector3.Zero; for (int v = 0; v < HISTORY_SIZE; ++v) { int vIndex = (mHistoryIndex - v + HISTORY_SIZE) % HISTORY_SIZE; dirAvg += mDirHistory[vIndex] * (HISTORY_SIZE - v); // Weight to favor more recent entries. } if (dirAvg.LengthSquared() != 0.0f) { dirAvg.Normalize(); } // avgTheta is a measure of how much each direction in the history varies with the average. float avgTheta = 0.0f; int numWeights = ((HISTORY_SIZE + 1) * HISTORY_SIZE) / 2; for (int v = 0; v < HISTORY_SIZE; ++v) { int vIndex = (mHistoryIndex - v + HISTORY_SIZE) % HISTORY_SIZE; avgTheta += mForceHistory[vIndex] == 0.0f ? 0.0f : (float)(Math.Acos(MathHelper.Clamp(Vector3.Dot(mDirHistory[vIndex], dirAvg), 0.0f, 1.0f))) * (float)(HISTORY_SIZE - v); } avgTheta /= (float)numWeights; // If avgTheta is large, the immediate forces are jittery, and we should use more history to smooth out the final result. // otherwise, the immediate force is more continuous and we can use fewer histories. int numHistoriesToUse = Math.Max((int)(Math.Min(avgTheta / MathHelper.Pi * 8.0f, 1.0f) * (float)HISTORY_SIZE), 1); // In addition, we give recent forces more weight using a simple arithmetic sequence. numWeights = ((numHistoriesToUse + 1) * numHistoriesToUse) / 2; res3D = Vector3.Zero; for (int v = 0; v < numHistoriesToUse; ++v) { int vIndex = (mHistoryIndex - v + HISTORY_SIZE) % HISTORY_SIZE; res3D += mDirHistory[vIndex] * (float)(numHistoriesToUse - v) * mForceHistory[vIndex]; } res3D /= (float)(numWeights); // Back again into controller space: controllerRotation = Matrix.Transpose(controllerRotation); res3D = Vector3.Transform(res3D, controllerRotation); result.X = res3D.X; result.Y = -res3D.Z; return(result * ForceScale); }
// Steer away from obstacles in the way. This method returns a zero vector if no correction is required. // It should be high priority and the steering from other behaviors should blend into the remaining space. // So if this returns a length 1.0f vector, avoiding the obstacle is most urgent and there is no room for other // steering. private Vector2 AvoidObstacles(Actor owner) { BipedControllerComponent bcc = owner.GetComponent <BipedControllerComponent>(ActorComponent.ComponentType.Control); // Conditions where we do not want to use this steering force. if (GetAngleFromVertical(bcc.Controller.Body.LinearVelocity) < MathHelper.PiOver4 || // We're probably falling... !bcc.Controller.SupportFinder.HasSupport || !bcc.Controller.SupportFinder.HasTraction) { return(Vector2.Zero); } // Sphere cast ahead along facing. List <RayCastResult> obstacles = new List <RayCastResult>(); SphereShape probe = new SphereShape(bcc.Controller.BodyRadius * 1.1f); RigidTransform probeStartPosition = new RigidTransform(bcc.Controller.Body.Position); // Add a small constant to the probe length because we want a minimum amount of forward probing, even if we are not moving. float probeLength = Math.Max(BepuVec3.Dot(bcc.Controller.Body.LinearVelocity, bcc.Controller.ViewDirection), 0.0f) + 1.0f; BepuVec3 probeSweep = bcc.Controller.ViewDirection * probeLength; ObstacleFilter filter = new ObstacleFilter(bcc.Controller.Body.CollisionInformation); GameResources.ActorManager.SimSpace.ConvexCast(probe, ref probeStartPosition, ref probeSweep, filter.Test, obstacles); RayCastDistanceComparer rcdc = new RayCastDistanceComparer(); obstacles.Sort(rcdc); BEPUutilities.Vector3 cross = BEPUutilities.Vector3.Zero; int obstacleIndex = 0; do { if (obstacles.Count == obstacleIndex) { return(Vector2.Zero); } cross = BEPUutilities.Vector3.Cross(bcc.Controller.ViewDirection, -obstacles[obstacleIndex++].HitData.Normal); }while (cross.X > 0.7f); // if cross.X > 0.7f, the obstacle is some kind of gentle ramp; ignore it. // dot will typically be negative and magnitude indicates how directly ahead the obstacle is. float dot = BEPUutilities.Vector3.Dot(bcc.Controller.ViewDirection, -obstacles[0].HitData.Normal); if (dot >= 0.0f) // The obstacle won't hinder us if we touch it. { return(Vector2.Zero); } // When cross.Y is positive, the object is generally to the right, so veer left (and vice versa). float directionSign = cross.Y >= 0.0f ? -1.0f : 1.0f; BEPUutilities.Vector2 result = BEPUutilities.Vector2.UnitX * directionSign * -dot; // Also scale response by how close the obstacle is. float distance = (obstacles[0].HitData.Location - bcc.Controller.Body.Position).Length(); result *= MathHelper.Clamp((1.0f - distance / probeLength), 0.0f, 1.0f); // / Math.Abs(dot); // So far the result is in terms of 'velocity space'. Rotate it to align with the controller facing. float velocityTheta = (float)(Math.Atan2(-probeSweep.X, -probeSweep.Z)); BEPUutilities.Matrix2x2 velocityWorld = SpaceUtils.Create2x2RotationMatrix(velocityTheta); float facingTheta = (float)(Math.Atan2(-bcc.Controller.HorizontalViewDirection.X, -bcc.Controller.HorizontalViewDirection.Z)); BEPUutilities.Matrix2x2 facingWorldInv = SpaceUtils.Create2x2RotationMatrix(facingTheta); facingWorldInv.Transpose(); // We want the transpose/inverse of the facing transform because we want to transform the movement into 'facing space'. return(BepuConverter.Convert(SpaceUtils.TransformVec2(SpaceUtils.TransformVec2(result, velocityWorld), facingWorldInv))); }