/// <summary> /// Returns the first hit of a given ray tested against the physics world, does filtering based on the given CollisionGroup only solid collisions will be reported /// </summary> /// <param name="ray">Ray to test against</param> /// <param name="length">Maximum distance to test</param> /// <param name="group">Collision Group to use for the ray, anything above "Normal" is ignored</param> /// <param name="outHitResult">The hit that was found if any</param> /// <returns>True if any object was hit</returns> public bool Raycast(ref Ray ray, float length, CollisionGroup group, ref SRaycastResult outHitResult) { return(Raycast(ref ray, length, GetSolidFilter(group), ref outHitResult)); }
static Player() { CollisionGroup.DefineCollisionRule(noCollisionGroup, GameModel.GhostGroup, CollisionRule.NoBroadPhase); CollisionGroup.DefineCollisionRule(noCollisionGroup, GameModel.NormalGroup, CollisionRule.NoBroadPhase); }
public TilemapCollisionComponent() { Group = CollisionGroup.None; }
private CollisionRule GetCollisionRuleWithGroup(ICollisionRulesOwner rulesOwner, CollisionGroup group) { if (rulesOwner.CollisionRules.Personal != CollisionRule.Defer) { return(rulesOwner.CollisionRules.Personal); } CollisionGroupPair pair = new CollisionGroupPair(group, rulesOwner.CollisionRules.Group); CollisionRules.CollisionGroupRules.TryGetValue(pair, out CollisionRule rule); return(rule); }
/// <summary> /// Check if the character's interaction area collides with the world object click area. /// The character also should not be dead. /// </summary> public virtual bool SharedIsInsideCharacterInteractionArea( ICharacter character, TWorldObject worldObject, bool writeToLog, CollisionGroup requiredCollisionGroup = null) { if (worldObject.IsDestroyed) { return(false); } try { this.VerifyGameObject(worldObject); } catch (Exception ex) { if (writeToLog) { Logger.Exception(ex); } else { Logger.Warning(ex.Message + " during " + nameof(SharedIsInsideCharacterInteractionArea)); } return(false); } if (character.GetPublicState <ICharacterPublicState>().IsDead || IsServer && !character.ServerIsOnline || ((IsServer || character.IsCurrentClientCharacter) && PlayerCharacter.GetPrivateState(character).IsDespawned)) { if (writeToLog) { Logger.Warning( $"Character cannot interact with {worldObject} - character is offline/despawned/dead.", character); } return(false); } bool isInsideInteractionArea; if (worldObject.PhysicsBody.HasShapes && worldObject.PhysicsBody.HasAnyShapeCollidingWithGroup(CollisionGroups.ClickArea)) { // check that the world object is inside the interaction area of the character using var objectsInCharacterInteractionArea = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea( character, writeToLog, requiredCollisionGroup); isInsideInteractionArea = false; if (objectsInCharacterInteractionArea is not null) { foreach (var t in objectsInCharacterInteractionArea.AsList()) { if (!ReferenceEquals(worldObject, t.PhysicsBody.AssociatedWorldObject)) { continue; } isInsideInteractionArea = true; break; } } } else if (worldObject.ProtoWorldObject is IProtoStaticWorldObject protoStaticWorldObject) { // the world object doesn't have click area collision shapes // check this object tile by tile // ensure at least one tile of this object is inside the character interaction area // ensure there is direct line of sight between player character and this tile var characterInteractionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault( s => s.CollisionGroup == CollisionGroups.CharacterInteractionArea); isInsideInteractionArea = false; foreach (var tileOffset in protoStaticWorldObject.Layout.TileOffsets) { var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape( sourceShape: characterInteractionAreaShape, targetShape: new RectangleShape( position: (worldObject.TilePosition + tileOffset).ToVector2D() + (0.01, 0.01), size: (0.98, 0.98), collisionGroup: CollisionGroups.ClickArea), sourceShapeOffset: character.PhysicsBody.Position); if (!penetration.HasValue) { // this tile is not inside the character interaction area continue; } // the tile is inside the character interaction area // check that there is a direct line between the character and the tile isInsideInteractionArea = true; break; } } else { isInsideInteractionArea = false; } if (!isInsideInteractionArea) { // the world object is outside the character interaction area if (writeToLog) { Logger.Warning( $"Character cannot interact with {worldObject} - outside the interaction area.", character); if (IsClient) { ClientOnCannotInteract(worldObject, CoreStrings.Notification_TooFar, isOutOfRange: true); } } return(false); } if (character.ProtoCharacter is PlayerCharacterSpectator) { // don't test for obstacles for spectator character return(true); } // check that there are no other objects on the way between them (defined by default layer) var physicsSpace = character.PhysicsBody.PhysicsSpace; var characterCenter = character.Position + character.PhysicsBody.CenterOffset; if (!ObstacleTestHelper.SharedHasObstaclesInTheWay(characterCenter, physicsSpace, worldObject, sendDebugEvents: writeToLog)) { return(true); } if (writeToLog) { Logger.Warning( $"Character cannot interact with {worldObject} - there are other objects on the way.", character); if (IsClient) { ClientOnCannotInteract(worldObject, CoreStrings.Notification_ObstaclesOnTheWay, isOutOfRange: true); } } return(false); }
/// <summary> /// Sweeps a sphere against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="radius">Radius of the sphere</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param> /// <param name="outSweepResult">Closest hit to the start location</param> /// <returns>True if any object was hit</returns> public bool SphereSweep(float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult) { SphereShape sphereShape = new SphereShape(radius); Quaternion rotation = Quaternion.Identity; return(ConvexSweep(sphereShape, ref startPos, ref rotation, ref sweep, group, ref outSweepResult)); }
/// <summary> /// Sweeps a capsule against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="length">Length of the capsule</param> /// <param name="radius">Radius of the capsule</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="rotation">Rotation of the shape during the sweep</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param> /// <param name="outSweepResult">Closest hit to the start location</param> /// <returns>True if any object was hit</returns> public bool CapsuleSweep(float length, float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult) { CapsuleShape capsuleShape = new CapsuleShape(length, radius); return(ConvexSweep(capsuleShape, ref startPos, ref rotation, ref sweep, group, ref outSweepResult)); }
public bool SharedValidatePlacement(ICharacter character, Vector2Ushort targetPosition, bool logErrors) { // check if there is a direct line of sight // check that there are no other objects on the way between them (defined by default layer) var physicsSpace = character.PhysicsBody.PhysicsSpace; var characterCenter = character.Position + character.PhysicsBody.CenterOffset; var worldObjectCenter = targetPosition.ToVector2D() + (0.5, 0.5); // local method for testing if there is an obstacle from current to the specified position bool TestHasObstacle(Vector2D toPosition) { using (var obstaclesOnTheWay = physicsSpace.TestLine( characterCenter, toPosition, CollisionGroup.GetDefault(), sendDebugEvent: false)) { foreach (var test in obstaclesOnTheWay) { var testPhysicsBody = test.PhysicsBody; if (testPhysicsBody.AssociatedProtoTile != null) { // obstacle tile on the way return(true); } var testWorldObject = testPhysicsBody.AssociatedWorldObject; if (testWorldObject == character) { // not an obstacle - it's the character or world object itself continue; } // obstacle object on the way return(true); } // no obstacles return(false); } } // let's test by casting rays from character center to the center of the planted bomb if (TestHasObstacle(worldObjectCenter)) { // has obstacle if (logErrors) { if (IsClient) { this.ClientShowCannotPlaceObstaclesOnTheWayNotification(); } else { Logger.Warning($"{character} cannot place {this} - obstacles on the way"); this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles()); } } return(false); } // validate distance to the character if (targetPosition.TileDistanceTo(character.TilePosition) > this.DeployDistanceMax) { // distance exceeded - too far if (logErrors) { if (IsClient) { this.ClientShowCannotPlaceTooFarNotification(); } else { Logger.Warning($"{character} cannot place {this} - too far"); this.CallClient(character, _ => _.ClientRemote_CannotPlaceTooFar()); } } return(false); } if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition, character, logErrors)) { // explosive static object placement requirements failed return(false); } return(true); }
/// <summary> /// /// </summary> /// <param name="body"></param> /// <param name="collisionGroup"></param> /// <param name="onCollision">If you return true, the collision will be resolved.</param> public Collider(Body body, CollisionGroup collisionGroup, Func <CollisionData, bool> onCollision) { this.body = body; this.collisionGroup = collisionGroup; this.onCollision = onCollision; }
public void CollidesWithGroup(CollisionGroup mask) { BroadphaseProxy.CollisionFilterGroup = mask; }
public bool BelongsToGroup(CollisionGroup group) { return((BroadphaseProxy.CollisionFilterGroup & group) != CollisionGroup.None); }
public void SetCollisionGroup(CollisionGroup group) { BroadphaseProxy.CollisionGroup = group; }
private static void SharedShotWeaponHitscan( ICharacter character, IProtoItemWeapon protoWeapon, Vector2D fromPosition, WeaponFinalCache weaponCache, Vector2D?customTargetPosition, IProtoCharacterCore characterProtoCharacter, double fireSpreadAngleOffsetDeg, CollisionGroup collisionGroup, bool isMeleeWeapon, IDynamicWorldObject characterCurrentVehicle, ProtoSkillWeapons protoWeaponSkill, PlayerCharacterSkills playerCharacterSkills, ITempList <IWorldObject> allHitObjects) { Vector2D toPosition; var rangeMax = weaponCache.RangeMax; if (customTargetPosition.HasValue) { var direction = customTargetPosition.Value - fromPosition; // ensure the max range is not exceeded direction = direction.ClampMagnitude(rangeMax); toPosition = fromPosition + direction; } else { toPosition = fromPosition + new Vector2D(rangeMax, 0) .RotateRad(characterProtoCharacter.SharedGetRotationAngleRad(character) + fireSpreadAngleOffsetDeg * Math.PI / 180.0); } using var lineTestResults = character.PhysicsBody.PhysicsSpace.TestLine( fromPosition: fromPosition, toPosition: toPosition, collisionGroup: collisionGroup); var damageMultiplier = 1d; var hitObjects = new List <WeaponHitData>(isMeleeWeapon ? 1 : lineTestResults.Count); var characterTileHeight = character.Tile.Height; if (IsClient || Api.IsEditor) { SharedEditorPhysicsDebugger.SharedVisualizeTestResults(lineTestResults, collisionGroup); } var isDamageRayStopped = false; foreach (var testResult in lineTestResults.AsList()) { var testResultPhysicsBody = testResult.PhysicsBody; var attackedProtoTile = testResultPhysicsBody.AssociatedProtoTile; if (attackedProtoTile != null) { if (attackedProtoTile.Kind != TileKind.Solid) { // non-solid obstacle - skip continue; } var attackedTile = IsServer ? Server.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position) : Client.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position); if (attackedTile.Height < characterTileHeight) { // attacked tile is below - ignore it continue; } // tile on the way - blocking damage ray isDamageRayStopped = true; var hitData = new WeaponHitData(testResult.PhysicsBody.Position + SharedOffsetHitWorldPositionCloserToTileHitboxCenter( testResultPhysicsBody, testResult.Penetration, isRangedWeapon: !isMeleeWeapon)); hitObjects.Add(hitData); weaponCache.ProtoWeapon .SharedOnHit(weaponCache, null, 0, hitData, out _); break; } var damagedObject = testResultPhysicsBody.AssociatedWorldObject; if (ReferenceEquals(damagedObject, character) || ReferenceEquals(damagedObject, characterCurrentVehicle)) { // ignore collision with self continue; } if (!(damagedObject.ProtoGameObject is IDamageableProtoWorldObject damageableProto)) { // shoot through this object continue; } // don't allow damage is there is no direct line of sight on physical colliders layer between the two objects if (SharedHasTileObstacle(character.Position, characterTileHeight, damagedObject, targetPosition: testResult.PhysicsBody.Position + testResult.PhysicsBody.CenterOffset)) { continue; } using (CharacterDamageContext.Create(attackerCharacter: character, damagedObject as ICharacter, protoWeaponSkill)) { if (!damageableProto.SharedOnDamage( weaponCache, damagedObject, damageMultiplier, damagePostMultiplier: 1.0, out var obstacleBlockDamageCoef, out var damageApplied)) { // not hit continue; } var hitData = new WeaponHitData(damagedObject, testResult.Penetration.ToVector2F()); weaponCache.ProtoWeapon .SharedOnHit(weaponCache, damagedObject, damageApplied, hitData, out var isDamageStop); if (isDamageStop) { obstacleBlockDamageCoef = 1; } if (IsServer) { if (damageApplied > 0 && damagedObject is ICharacter damagedCharacter) { CharacterUnstuckSystem.ServerTryCancelUnstuckRequest(damagedCharacter); } if (damageApplied > 0) { // give experience for damage protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills, damagedObject, damageApplied); } } if (obstacleBlockDamageCoef < 0 || obstacleBlockDamageCoef > 1) { Logger.Error( "Obstacle block damage coefficient should be >= 0 and <= 1 - wrong calculation by " + damageableProto); break; } hitObjects.Add(hitData); if (isMeleeWeapon) { // currently melee weapon could attack only one object on the ray isDamageRayStopped = true; break; } damageMultiplier *= 1.0 - obstacleBlockDamageCoef; if (damageMultiplier <= 0) { // target blocked the damage ray isDamageRayStopped = true; break; } } } var shotEndPosition = GetShotEndPosition(isDamageRayStopped, hitObjects, toPosition, isRangedWeapon: !isMeleeWeapon); if (hitObjects.Count == 0) { protoWeapon.SharedOnMiss(weaponCache, shotEndPosition); } SharedCallOnWeaponHitOrTrace(character, protoWeapon, weaponCache.ProtoAmmo, shotEndPosition, hitObjects, endsWithHit: isDamageRayStopped); foreach (var entry in hitObjects) { if (!entry.IsCliffsHit && !allHitObjects.Contains(entry.WorldObject)) { allHitObjects.Add(entry.WorldObject); } } }
public void Register(CollisionGroup a, CollisionGroup b, Action <ICollider, ICollider> action) { lookup[Tuple.Create(a, b)] = action; }
/// <summary> /// Sweeps a convex shape against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="shape">Shape to sweep</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="rotation">Rotation of the shape during the sweep</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param> /// <param name="outSweepResult">Closest hit to the start location</param> /// <returns>True if any object was hit</returns> public bool ConvexSweep(ConvexShape shape, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult) { return(ConvexSweep(shape, ref startPos, ref rotation, ref sweep, GetSolidFilter(group), ref outSweepResult)); }
/// <summary> /// /// </summary> /// <param name="body"></param> /// <param name="pos">Position relitive to body Pos.</param> /// <param name="radius"></param> /// <param name="OnCollision"></param> public CircleCollider(Body body, CollisionGroup collisionGroup, Vector2 pos, float radius, Func <CollisionData, bool> onCollision) : base(body, collisionGroup, onCollision) { this.pos = pos; this.radius = radius; }
/// <summary> /// Sweeps a convex shape against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="shape">Shape to sweep</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="rotation">Rotation of the shape during the sweep</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param> /// <param name="outSweepResults">All overlaps on the sweep path</param> /// <returns>True if any object was hit</returns> public bool MultiSweep(ConvexShape shape, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults) { RigidTransform startTransform = new RigidTransform(startPos.ToBepu(), rotation.ToBepu()); Vector3 sweepVector = sweep.ToBepu(); List <RayCastResult> physicsResults = new List <RayCastResult>(); if (m_physicSpace.ConvexCast(shape, ref startTransform, ref sweepVector, GetOverlapFilter(group), physicsResults)) { foreach (RayCastResult physicsResult in physicsResults) { if (physicsResult.HitObject.Tag is CEntity gameEntity) { CollisionRule rule = GetCollisionRuleWithGroup(physicsResult.HitObject, group); SRaycastResult gameResult = new SRaycastResult() { HitEntity = gameEntity, Location = physicsResult.HitData.Location.ToSharp(), Normal = -physicsResult.HitData.Normal.ToSharp(), Distance = physicsResult.HitData.T, bIsSolidHit = rule <= CollisionRule.Normal }; outSweepResults.Add(gameResult); } } return(outSweepResults.Count > 0); } return(false); }
public static void ServerProcessExplosionCircle( Vector2D positionEpicenter, IPhysicsSpace physicsSpace, double damageDistanceMax, WeaponFinalCache weaponFinalCache, bool damageOnlyDynamicObjects, bool isDamageThroughObstacles, Func <double, double> callbackCalculateDamageCoefByDistanceForStaticObjects, Func <double, double> callbackCalculateDamageCoefByDistanceForDynamicObjects, [CanBeNull] CollisionGroup collisionGroup = null) { var playerCharacterSkills = weaponFinalCache.Character?.SharedGetSkills(); var protoWeaponSkill = playerCharacterSkills is not null ? weaponFinalCache.ProtoWeapon?.WeaponSkillProto : null; // collect all damaged objects via physics space var damageCandidates = new HashSet <IWorldObject>(); collisionGroup ??= CollisionGroups.Default; var defaultCollisionGroup = collisionGroup; CollectDamagedPhysicalObjects(defaultCollisionGroup); CollectDamagedPhysicalObjects(CollisionGroups.HitboxMelee); CollectDamagedPhysicalObjects(CollisionGroups.HitboxRanged); void CollectDamagedPhysicalObjects(CollisionGroup collisionGroup) { using var testResults = physicsSpace.TestCircle(positionEpicenter, radius: damageDistanceMax, collisionGroup: collisionGroup); foreach (var testResult in testResults.AsList()) { var testResultPhysicsBody = testResult.PhysicsBody; var damagedObject = testResultPhysicsBody.AssociatedWorldObject; if (damageOnlyDynamicObjects && damagedObject is IStaticWorldObject) { continue; } if (!(damagedObject?.ProtoWorldObject is IDamageableProtoWorldObject)) { // non-damageable world object continue; } damageCandidates.Add(damagedObject); } } if (!damageOnlyDynamicObjects) { // Collect all the damageable static objects in the explosion radius // which don't have a collider colliding with the collision group. var startTilePosition = positionEpicenter.ToVector2Ushort(); var damageDistanceMaxRounded = (int)damageDistanceMax; var damageDistanceMaxSqr = damageDistanceMax * damageDistanceMax; var minTileX = startTilePosition.X - damageDistanceMaxRounded; var minTileY = startTilePosition.Y - damageDistanceMaxRounded; var maxTileX = startTilePosition.X + damageDistanceMaxRounded; var maxTileY = startTilePosition.Y + damageDistanceMaxRounded; for (var x = minTileX; x <= maxTileX; x++) { for (var y = minTileY; y <= maxTileY; y++) { if (x < 0 || x > ushort.MaxValue || y < 0 || y > ushort.MaxValue) { continue; } if (new Vector2Ushort((ushort)x, (ushort)y) .TileSqrDistanceTo(startTilePosition) > damageDistanceMaxSqr) { // too far continue; } var tileObjects = Api.Server.World.GetStaticObjects(new Vector2Ushort((ushort)x, (ushort)y)); if (tileObjects.Count == 0) { continue; } foreach (var tileObject in tileObjects) { if (!(tileObject.ProtoStaticWorldObject is IDamageableProtoWorldObject)) { // non-damageable continue; } if (tileObject.PhysicsBody.HasAnyShapeCollidingWithGroup(defaultCollisionGroup)) { // has a collider colliding with the collision group so we ignore this continue; } damageCandidates.Add(tileObject); } } } } // order by distance to explosion center var orderedDamageCandidates = damageCandidates.OrderBy( ServerExplosionGetDistanceToEpicenter(positionEpicenter, collisionGroup)); var hitCharacters = new List <WeaponHitData>(); // process all damage candidates foreach (var damagedObject in orderedDamageCandidates) { if (!isDamageThroughObstacles && ServerHasObstacleForExplosion(physicsSpace, positionEpicenter, damagedObject, defaultCollisionGroup)) { continue; } var distanceToDamagedObject = ServerCalculateDistanceToDamagedObject(positionEpicenter, damagedObject); var damagePreMultiplier = damagedObject is IDynamicWorldObject ? callbackCalculateDamageCoefByDistanceForDynamicObjects(distanceToDamagedObject) : callbackCalculateDamageCoefByDistanceForStaticObjects(distanceToDamagedObject); damagePreMultiplier = MathHelper.Clamp(damagePreMultiplier, 0, 1); var damageableProto = (IDamageableProtoWorldObject)damagedObject.ProtoGameObject; damageableProto.SharedOnDamage( weaponFinalCache, damagedObject, damagePreMultiplier, damagePostMultiplier: 1.0, out _, out var damageApplied); if (damageApplied > 0 && damagedObject is ICharacter damagedCharacter) { hitCharacters.Add(new WeaponHitData(damagedCharacter, (0, damagedCharacter .ProtoCharacter.CharacterWorldWeaponOffsetRanged))); } if (damageApplied > 0) { // give experience for damage protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills, damagedObject, damageApplied); } weaponFinalCache.ProtoExplosive?.ServerOnObjectHitByExplosion(damagedObject, damageApplied, weaponFinalCache); (weaponFinalCache.ProtoWeapon as ProtoItemMobWeaponNova)? .ServerOnObjectHitByNova(damagedObject, damageApplied, weaponFinalCache); } if (hitCharacters.Count == 0) { return; } // display damages on clients in scope of every damaged object var observers = new HashSet <ICharacter>(); using var tempList = Api.Shared.GetTempList <ICharacter>(); foreach (var hitObject in hitCharacters) { if (hitObject.WorldObject is ICharacter damagedCharacter && !damagedCharacter.IsNpc) { // notify the damaged character observers.Add(damagedCharacter); } Server.World.GetScopedByPlayers(hitObject.WorldObject, tempList); tempList.Clear(); observers.AddRange(tempList.AsList()); } // add all observers within the sound radius var eventNetworkRadius = (byte)Math.Max( 20, Math.Ceiling(1.5 * damageDistanceMax)); tempList.Clear(); Server.World.GetCharactersInRadius(positionEpicenter.ToVector2Ushort(), tempList, radius: eventNetworkRadius, onlyPlayers: true); observers.AddRange(tempList.AsList()); if (observers.Count > 0) { Instance.CallClient(observers, _ => _.ClientRemote_OnCharactersHitByExplosion(hitCharacters)); } }
/// <summary> /// Sweeps a sphere against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="radius">Radius of the sphere</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param> /// <param name="outSweepResults">All overlaps on the sweep path</param> /// <returns>True if any object was hit</returns> public bool SphereMultiSweep(float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults) { SphereShape sphereShape = new SphereShape(radius); Quaternion rotation = Quaternion.Identity; return(MultiSweep(sphereShape, ref startPos, ref rotation, ref sweep, group, outSweepResults)); }
public void LoadLevel(String fileName) { // Load level data GameLevelContent levelData = Game.Content.Load <GameLevelContent>(fileName); int blocksCount = levelData.BlocksCount(); // List of blocks chapes for compound body List <CompoundShapeEntry> shapes = new List <CompoundShapeEntry>(); // Create block groups and block physics foreach (BlockGroupData blockGroup in levelData.BlockGroups) { // Create block group LoadBlockType(blockGroup.BlockTypeName, blocksCount); // Create physical representation of blocks in this group Vector3 scale; Quaternion rotation; Vector3 translation; foreach (BlockData blockData in blockGroup.Blocks) { // Extract size, position and orientation values from block data transform blockData.Transform.Decompose(out scale, out rotation, out translation); blockData.Scale = scale; // Create physical shape of the block to be part of compound body of blocks BoxShape blockShape = new BoxShape(scale.X, scale.Y, scale.Z); // Create compound shape entry for compund body of blocks CompoundShapeEntry entry = new CompoundShapeEntry(blockShape, new RigidTransform(translation, rotation)); shapes.Add(entry); } } // Create compound body compoundBody = new CompoundBody(shapes, COMPOUND_BODY_MASS); compoundBody.PositionUpdateMode = BEPUphysics.PositionUpdating.PositionUpdateMode.Continuous; compoundBody.AngularDamping = COMPOUND_BODY_ANGULAR_DAMPING; // Compound body has Position and LocalPosition (in Collision information) // Position property is position of mass center in global space - it is calculated automatically. // LocalPosition property is position of geometry of the body in its local space. // So in order to create compound body which is rotated around desired position ((0,0,0) for now) // We should switch Position and LocalPosition properties of our compound body. compoundBody.CollisionInformation.LocalPosition = compoundBody.Position; compoundBody.Position = Vector3.Zero; // Add constraint prevents compound body from moving constraint = new MaximumLinearSpeedConstraint(compoundBody, 0.0f); // Create collision group for removed blocks removedBlocksGroup = new CollisionGroup(); // Create blocks int childCollidableIndex = 0; foreach (BlockGroupData blockGroup in levelData.BlockGroups) { Matrix localPosTransform = Matrix.CreateTranslation(compoundBody.CollisionInformation.LocalPosition); foreach (BlockData blockData in blockGroup.Blocks) { // Obtain block type and instanced mesh for the block BlockType blockType = blockTypes[blockGroup.BlockTypeName]; InstancedMesh <VertexData> instancedMesh = blockInstancedMeshes[blockGroup.BlockTypeName]; // Obtain physics body (a part of compound body) for the block CompoundChild child = compoundBody.CollisionInformation.Children[childCollidableIndex]; // Create instance of the block in instanced mesh InstancedMesh <VertexData> .Instance instance = instancedMesh.AppendInstance(Matrix.CreateScale(blockData.Scale) * child.Entry.LocalTransform.Matrix * localPosTransform); // Store new block instance to the list Block block = new Block(blockType, instance, child); blocks.Add(block); block.Scale = blockData.Scale; childCollidableIndex++; } } // Add compound body and its constraints to physics space space.Add(compoundBody); space.Add(constraint); }
/// <summary> /// Sweeps a capsule against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long /// </summary> /// <param name="length">Length of the capsule</param> /// <param name="radius">Radius of the capsule</param> /// <param name="startPos">Location to start the sweep from</param> /// <param name="rotation">Rotation of the shape during the sweep</param> /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param> /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param> /// <param name="outSweepResults">All overlaps on the sweep path</param> /// <returns>True if any object was hit</returns> public bool CapsuleMultiSweep(float length, float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults) { CapsuleShape capsuleShape = new CapsuleShape(length, radius); return(MultiSweep(capsuleShape, ref startPos, ref rotation, ref sweep, group, outSweepResults)); }
public static bool SharedHasObstaclesOnTheWay( Vector2D fromPosition, IPhysicsSpace physicsSpace, [CanBeNull] IWorldObject worldObject, Vector2D worldObjectCenter, Vector2D worldObjectPointClosestToCharacter, bool sendDebugEvents, CollisionGroup collisionGroup) { // let's test by casting rays from "fromPosition" (usually it's the character's center) to: // 0) world object center // 1) world object point closest to the character // 2) combined - take X from center, take Y from closest // 3) combined - take X from closest, take Y from center if (TestHasObstacle(worldObjectCenter) && TestHasObstacle(worldObjectPointClosestToCharacter) && TestHasObstacle((worldObjectCenter.X, worldObjectPointClosestToCharacter.Y)) && TestHasObstacle((worldObjectPointClosestToCharacter.X, worldObjectCenter.Y))) { // has obstacle return(true); } return(false); // local method for testing if there is an obstacle from current to the specified position bool TestHasObstacle(Vector2D toPosition) { using var obstaclesOnTheWay = physicsSpace.TestLine( fromPosition, toPosition, collisionGroup, sendDebugEvent: sendDebugEvents); foreach (var test in obstaclesOnTheWay.AsList()) { var testPhysicsBody = test.PhysicsBody; if (testPhysicsBody.AssociatedProtoTile is not null) { // obstacle tile on the way return(true); } var testWorldObject = testPhysicsBody.AssociatedWorldObject; if (ReferenceEquals(testWorldObject, worldObject)) { // not an obstacle - it's the target object itself // stop checking collisions as we've reached the target object return(false); } if (testWorldObject is IDynamicWorldObject) { // dynamic world objects are not assumed as an obstacle continue; } // no need for this check anymore as we're checking for general "is ICharacter" above //if (ReferenceEquals(testWorldObject, character)) //{ // // not an obstacle - it's the player's character itself // continue; //} if (!testWorldObject.ProtoWorldObject .SharedIsAllowedObjectToInteractThrough(testWorldObject)) { // obstacle object on the way return(true); } } // no obstacles return(false); } }
public void ApplyPhysicsData(BsonDocument doc) { if (doc.ContainsKey("ph_pos")) { SetPosition(Location.FromDoubleBytes(doc["ph_pos"].AsBinary, 0)); } if (doc.ContainsKey("ph_vel")) { SetVelocity(Location.FromDoubleBytes(doc["ph_vel"].AsBinary, 0)); } if (doc.ContainsKey("ph_avel")) { SetAngularVelocity(Location.FromDoubleBytes(doc["ph_avel"].AsBinary, 0)); } if (doc.ContainsKey("ph_ang_x") && doc.ContainsKey("ph_ang_y") && doc.ContainsKey("ph_ang_z") && doc.ContainsKey("ph_ang_w")) { double ax = (double)doc["ph_ang_x"].AsDouble; double ay = (double)doc["ph_ang_y"].AsDouble; double az = (double)doc["ph_ang_z"].AsDouble; double aw = (double)doc["ph_ang_w"].AsDouble; SetOrientation(new BEPUutilities.Quaternion(ax, ay, az, aw)); } if (doc.ContainsKey("ph_grav")) { SetGravity(Location.FromDoubleBytes(doc["ph_grav"].AsBinary, 0)); } if (doc.ContainsKey("ph_bounce")) { SetBounciness((double)doc["ph_bounce"].AsDouble); } if (doc.ContainsKey("ph_frict")) { SetFriction((double)doc["ph_frict"].AsDouble); } if (doc.ContainsKey("ph_mass")) { SetMass((double)doc["ph_mass"].AsDouble); } if (doc.ContainsKey("ph_cg")) { int cg = doc["ph_cg"].AsInt32; if (cg == 0) { CGroup = CollisionUtil.NonSolid; } else if (cg == 1) { CGroup = CollisionUtil.Solid; } else if (cg == 2) { CGroup = CollisionUtil.Player; } else if (cg == 3) { CGroup = CollisionUtil.Item; } else // cg == 4 { CGroup = CollisionUtil.Water; } } if (doc.ContainsKey("ph_flag")) { int flags = doc["ph_flag"].AsInt32; Visible = (flags & 1) == 1; GenBlockShadow = (flags & 2) == 2; TransmitMe = (flags & 128) == 128; } }
static Actor() { CollisionGroup.DefineCollisionRule(staticObjects, staticObjects, CollisionRule.NoBroadPhase); CollisionGroup.DefineCollisionRule(staticObjects, dynamicObjects, CollisionRule.Normal); }
public static IEntity?SpawnIfUnobstructed( this IEntityManager entityManager, string?prototypeName, EntityCoordinates coordinates, CollisionGroup collisionLayer, in Box2?box = null,
public RectangleCollider(Body body, CollisionGroup collisionGroup, Vector2 pos, Vector2 dimensions, Func <CollisionData, bool> OnCollision) : base(body, collisionGroup, OnCollision) { this.dimensions = dimensions; this.pos = pos; }