mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Raycasting + Turret AI (#532)
* Ray -> Box intersection works. * Turret AI finished. * Turret Works :D * Light masks can now be rotated. * Shoddy angle MoveTowards works. * Shoddy Vector2 MoveTowards works. * And pretty broken Quaternion version.. * Slept on it, rotation works good enough now. * Fixed nuget dependencies. * Moved AimShootLifeProcessor.cs to content.
This commit is contained in:
committed by
Pieter-Jan Briers
parent
2fb6f27cd1
commit
3a5ea35c0b
@@ -14,6 +14,7 @@ namespace SS14.Client.Graphics.Lighting
|
||||
Color Color { get; set; }
|
||||
Texture Mask { get; set; }
|
||||
LocalCoordinates Coordinates { get; set; }
|
||||
Angle Rotation { get; set; }
|
||||
RenderImage RenderTarget { get; }
|
||||
LightState LightState { get; set; }
|
||||
LightMode LightMode { get; set; }
|
||||
|
||||
@@ -43,7 +43,9 @@ namespace SS14.Client.Graphics.Lighting
|
||||
Calculated = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Angle Rotation { get; set; }
|
||||
|
||||
public LightMode LightMode { get; set; }
|
||||
|
||||
public LightState LightState
|
||||
|
||||
@@ -93,6 +93,9 @@ namespace SS14.Client.Graphics.Lighting
|
||||
// draw light mask onto lightRT
|
||||
var sprite = new Sprite(lightMask);
|
||||
sprite.Scale = new Vector2((float)lightRt.Width / maskSize.X, (float)lightRt.Height / maskSize.Y);
|
||||
sprite.Origin = new Vector2(sprite.LocalBounds.Width / 2, sprite.LocalBounds.Height / 2);
|
||||
sprite.Position = sprite.Origin * sprite.Scale;
|
||||
sprite.Rotation = -light.Rotation + Math.PI / 2; // convert our angle to sfml angle (negative may be because light mask tex is flipped);
|
||||
lightRt.Draw(sprite);
|
||||
}
|
||||
lightRt.EndDrawing();
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace SS14.Client.GameObjects
|
||||
RegisterReference<ClickableComponent, IClickableComponent>();
|
||||
|
||||
Register<OccluderComponent>();
|
||||
|
||||
RegisterIgnore("AiController");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,20 @@ namespace SS14.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the light mask should automatically rotate with the entity. (like a flashlight)
|
||||
/// </summary>
|
||||
public bool MaskAutoRotate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Local rotation of the light mask around the center origin
|
||||
/// </summary>
|
||||
public Angle Rotation
|
||||
{
|
||||
get => Light.Rotation;
|
||||
set => Light.Rotation = value;
|
||||
}
|
||||
|
||||
public int Radius
|
||||
{
|
||||
get => Light.Radius;
|
||||
@@ -111,6 +125,11 @@ namespace SS14.Client.GameObjects
|
||||
{
|
||||
ModeClass = LightModeClass.Constant;
|
||||
}
|
||||
|
||||
if (mapping.TryGetNode("autoRot", out node))
|
||||
{
|
||||
MaskAutoRotate = node.AsBool();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -153,6 +172,14 @@ namespace SS14.Client.GameObjects
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var worldRotation = Owner.GetComponent<TransformComponent>().WorldRotation;
|
||||
if (MaskAutoRotate && Light.Rotation != worldRotation)
|
||||
{
|
||||
Light.Rotation = worldRotation;
|
||||
Light.Calculated = false;
|
||||
}
|
||||
|
||||
Light.Update(frameTime);
|
||||
}
|
||||
|
||||
|
||||
26
SS14.Server/AI/AiLogicProcessor.cs
Normal file
26
SS14.Server/AI/AiLogicProcessor.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace SS14.Server.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all AI Processors.
|
||||
/// </summary>
|
||||
public abstract class AiLogicProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Radius in meters that the AI can "see".
|
||||
/// </summary>
|
||||
public float VisionRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity this AI is controlling.
|
||||
/// </summary>
|
||||
public IEntity SelfEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gives life to the AI.
|
||||
/// </summary>
|
||||
/// <param name="frameTime">Time since last update in seconds.</param>
|
||||
public abstract void Update(float frameTime);
|
||||
}
|
||||
}
|
||||
25
SS14.Server/AI/AiLogicProcessorAttribute.cs
Normal file
25
SS14.Server/AI/AiLogicProcessorAttribute.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace SS14.Server.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to mark a class as a LogicProcessor for the AI system.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public class AiLogicProcessorAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this LogicProcessor in serialized files.
|
||||
/// </summary>
|
||||
public string SerializeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this Attribute.
|
||||
/// </summary>
|
||||
/// <param name="serializeName">Name of this LogicProcessor in serialized files.</param>
|
||||
public AiLogicProcessorAttribute(string serializeName)
|
||||
{
|
||||
SerializeName = serializeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using SS14.Server.AI;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.Serialization;
|
||||
|
||||
namespace SS14.Server.GameObjects.Components
|
||||
{
|
||||
public class AiControllerComponent : Component, IMoverComponent
|
||||
{
|
||||
private string _logicName;
|
||||
private AiLogicProcessor processor;
|
||||
private float _visionRadius;
|
||||
|
||||
public override string Name => "AiController";
|
||||
|
||||
public string LogicName => _logicName;
|
||||
public AiLogicProcessor Processor { get; set; }
|
||||
|
||||
public float VisionRadius
|
||||
{
|
||||
get => _visionRadius;
|
||||
set => _visionRadius = value;
|
||||
}
|
||||
|
||||
public override void ExposeData(EntitySerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _logicName, "logic", null);
|
||||
serializer.DataField(ref _visionRadius, "vision", 8.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,8 @@ namespace SS14.Server.GameObjects
|
||||
RebuildMatrices();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public Angle WorldRotation
|
||||
{
|
||||
get
|
||||
|
||||
69
SS14.Server/GameObjects/EntitySystems/AiSystem.cs
Normal file
69
SS14.Server/GameObjects/EntitySystems/AiSystem.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Server.AI;
|
||||
using SS14.Server.GameObjects.Components;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.System;
|
||||
using SS14.Shared.Interfaces.Reflection;
|
||||
using SS14.Shared.IoC;
|
||||
|
||||
namespace SS14.Server.GameObjects.EntitySystems
|
||||
{
|
||||
internal class AiSystem : EntitySystem
|
||||
{
|
||||
private Dictionary<string, Type> _processorTypes = new Dictionary<string, Type>();
|
||||
|
||||
public AiSystem()
|
||||
{
|
||||
// register entity query
|
||||
EntityQuery = new ComponentEntityQuery
|
||||
{
|
||||
OneSet = new List<Type>
|
||||
{
|
||||
typeof(AiControllerComponent),
|
||||
},
|
||||
};
|
||||
|
||||
var reflectionMan = IoCManager.Resolve<IReflectionManager>();
|
||||
var processors = reflectionMan.GetAllChildren<AiLogicProcessor>();
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
var att = (AiLogicProcessorAttribute)Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute));
|
||||
if (att != null)
|
||||
{
|
||||
_processorTypes.Add(att.SerializeName, processor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var entities = EntityManager.GetEntities(EntityQuery);
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var aiComp = entity.GetComponent<AiControllerComponent>();
|
||||
if (aiComp.Processor == null)
|
||||
{
|
||||
aiComp.Processor = CreateProcessor(aiComp.LogicName);
|
||||
aiComp.Processor.SelfEntity = entity;
|
||||
aiComp.Processor.VisionRadius = aiComp.VisionRadius;
|
||||
}
|
||||
|
||||
var processor = aiComp.Processor;
|
||||
|
||||
processor.Update(frameTime);
|
||||
}
|
||||
}
|
||||
|
||||
private AiLogicProcessor CreateProcessor(string name)
|
||||
{
|
||||
if (_processorTypes.TryGetValue(name, out var type))
|
||||
{
|
||||
return (AiLogicProcessor) Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
// processor needs to inherit AiLogicProcessor, and needs an AiLogicProcessorAttribute to define the YAML name
|
||||
throw new ArgumentException($"Processor type {name} could not be found.", nameof(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using SS14.Server.GameObjects.Components.Container;
|
||||
using SS14.Server.GameObjects.Components;
|
||||
using SS14.Server.GameObjects.Components.Container;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -38,6 +39,8 @@ namespace SS14.Server.GameObjects
|
||||
|
||||
Register<ContainerManagerComponent>();
|
||||
RegisterReference<ContainerManagerComponent, IContainerManager>();
|
||||
|
||||
Register<AiControllerComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace SS14.Server.GameObjects
|
||||
return ForceSpawnEntityAt(entityType, new LocalCoordinates(position, map.FindGridAt(position)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityState> GetEntityStates()
|
||||
{
|
||||
var stateEntities = new List<EntityState>();
|
||||
@@ -85,6 +86,7 @@ namespace SS14.Server.GameObjects
|
||||
return stateEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveGridEntities(EntitySerializer serializer, GridId gridId)
|
||||
{
|
||||
// serialize all entities to disk
|
||||
@@ -101,6 +103,7 @@ namespace SS14.Server.GameObjects
|
||||
|
||||
#region EntityGetters
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Box2 position)
|
||||
{
|
||||
foreach (var entity in GetEntities())
|
||||
@@ -124,6 +127,7 @@ namespace SS14.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(MapId mapId, Vector2 position)
|
||||
{
|
||||
foreach (var entity in GetEntities())
|
||||
@@ -147,11 +151,13 @@ namespace SS14.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(LocalCoordinates position)
|
||||
{
|
||||
return GetEntitiesIntersecting(position.MapID, position.ToWorld().Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesIntersecting(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent<BoundingBoxComponent>(out var component))
|
||||
@@ -164,18 +170,21 @@ namespace SS14.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesInRange(LocalCoordinates position, float range)
|
||||
{
|
||||
var aabb = new Box2(position.Position - new Vector2(range / 2, range / 2), position.Position + new Vector2(range / 2, range / 2));
|
||||
return GetEntitiesIntersecting(position.MapID, aabb);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesInRange(MapId mapID, Box2 box, float range)
|
||||
{
|
||||
var aabb = new Box2(box.Left-range, box.Top+range, box.Right+range, box.Bottom-range);
|
||||
var aabb = new Box2(box.Left-range, box.Top-range, box.Right+range, box.Bottom+range);
|
||||
return GetEntitiesIntersecting(mapID, aabb);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IEntity> GetEntitiesInRange(IEntity entity, float range)
|
||||
{
|
||||
if (entity.TryGetComponent<BoundingBoxComponent>(out var component))
|
||||
@@ -191,6 +200,7 @@ namespace SS14.Server.GameObjects
|
||||
|
||||
#endregion LocationGetters
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
@@ -96,6 +96,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AI\AiLogicProcessor.cs" />
|
||||
<Compile Include="AI\AiLogicProcessorAttribute.cs" />
|
||||
<Compile Include="ClientConsoleHost\Commands\ChatCommands.cs" />
|
||||
<Compile Include="ClientConsoleHost\Commands\MapCommands.cs" />
|
||||
<Compile Include="ClientConsoleHost\Commands\PlayerCommands.cs" />
|
||||
@@ -103,6 +105,8 @@
|
||||
<Compile Include="ClientConsoleHost\Commands\SysCommands.cs" />
|
||||
<Compile Include="GameObjects\Components\Container\Container.cs" />
|
||||
<Compile Include="GameObjects\Components\Container\ContainerManagerComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Mover\AiControllerComponent.cs" />
|
||||
<Compile Include="GameObjects\EntitySystems\AiSystem.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\IServerTransformComponent.cs" />
|
||||
<Compile Include="Interfaces\Log\IServerLogManager.cs" />
|
||||
<Compile Include="Interfaces\Maps\IMapLoader.cs" />
|
||||
|
||||
@@ -71,6 +71,12 @@ namespace SS14.Shared.GameObjects
|
||||
EntityNetworkManager = networkManager;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsValid()
|
||||
{
|
||||
return !Deleted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the entity's UID. This can only be called once.
|
||||
/// </summary>
|
||||
|
||||
@@ -38,7 +38,13 @@ namespace SS14.Shared.Interfaces.GameObjects
|
||||
/// The prototype that was used to create this entity.
|
||||
/// </summary>
|
||||
EntityPrototype Prototype { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this entity is still valid.
|
||||
/// </summary>
|
||||
/// <returns>True if this entity is still valid.</returns>
|
||||
bool IsValid();
|
||||
|
||||
/// <summary>
|
||||
/// "Matches" this entity with the provided entity query, returning whether or not the query matched.
|
||||
/// This is effectively equivalent to calling <see cref="IEntityQuery.Match(IEntity)" /> with this entity.
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace SS14.Shared.Interfaces.Physics
|
||||
{
|
||||
public interface ICollidable
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that this collidable represents.
|
||||
/// </summary>
|
||||
IEntity Owner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// AABB of this entity in world space.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.Physics;
|
||||
|
||||
namespace SS14.Shared.Interfaces.Physics
|
||||
{
|
||||
@@ -18,5 +20,13 @@ namespace SS14.Shared.Interfaces.Physics
|
||||
void AddCollidable(ICollidable collidable);
|
||||
void RemoveCollidable(ICollidable collidable);
|
||||
void UpdateCollidable(ICollidable collidable);
|
||||
|
||||
/// <summary>
|
||||
/// Casts a ray in the world and returns the first thing it hit.
|
||||
/// </summary>
|
||||
/// <param name="ray">Ray to cast in the world.</param>
|
||||
/// <param name="maxLength">Maximum length of the ray in meters.</param>
|
||||
/// <returns>Owning entity of the object that was hit, or null if nothing was hit.</returns>
|
||||
RayCastResults IntersectRay(Ray ray, float maxLength = 50);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,16 @@ namespace SS14.Shared.Maths
|
||||
Theta = theta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of an angle from an (un)normalized direction vector.
|
||||
/// </summary>
|
||||
/// <param name="dir"></param>
|
||||
public Angle(Vector2 dir)
|
||||
{
|
||||
dir = dir.Normalized;
|
||||
Theta = Math.Atan2(dir.Y, dir.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this angle to a unit direction vector.
|
||||
/// </summary>
|
||||
|
||||
@@ -19,12 +19,12 @@ namespace SS14.Shared.Maths
|
||||
public float Height => Math.Abs(Top - Bottom);
|
||||
public Vector2 Size => new Vector2(Width, Height);
|
||||
|
||||
public Box2(Vector2 topLeft, Vector2 bottomRight)
|
||||
public Box2(Vector2 leftTop, Vector2 rightBottom)
|
||||
{
|
||||
Left = topLeft.X;
|
||||
Top = topLeft.Y;
|
||||
Bottom = bottomRight.Y;
|
||||
Right = bottomRight.X;
|
||||
Left = leftTop.X;
|
||||
Top = leftTop.Y;
|
||||
Right = rightBottom.X;
|
||||
Bottom = rightBottom.Y;
|
||||
}
|
||||
|
||||
public Box2(float left, float top, float right, float bottom)
|
||||
@@ -57,10 +57,7 @@ namespace SS14.Shared.Maths
|
||||
|
||||
public bool Encloses(Box2 inner)
|
||||
{
|
||||
return Left <= inner.Left
|
||||
&& inner.Right <= Right
|
||||
&& Top <= inner.Top
|
||||
&& inner.Bottom <= Bottom;
|
||||
return !(Left <= inner.Left) || !(inner.Right <= Right) || !(Top <= inner.Top) || !(inner.Bottom <= Bottom);
|
||||
}
|
||||
|
||||
public bool Contains(float x, float y)
|
||||
@@ -71,9 +68,7 @@ namespace SS14.Shared.Maths
|
||||
public bool Contains(Vector2 point, bool closedRegion = true)
|
||||
{
|
||||
var xOK = closedRegion == Left <= Right ? point.X >= Left != point.X > Right : point.X > Left != point.X >= Right;
|
||||
|
||||
var yOK = closedRegion == Top <= Bottom ? point.Y >= Top != point.Y > Bottom : point.Y > Top != point.Y >= Bottom;
|
||||
|
||||
return xOK && yOK;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace SS14.Shared.Maths
|
||||
{
|
||||
private const int LookupSize = 1024 * 64; //has to be power of 2
|
||||
private static readonly float[] getSin, getCos;
|
||||
public const float RadToDeg = (float)(180.0 / Math.PI);
|
||||
public const float DegToRad = (float)(Math.PI / 180.0);
|
||||
|
||||
static FloatMath()
|
||||
{
|
||||
@@ -50,7 +52,17 @@ namespace SS14.Shared.Maths
|
||||
cos = getCos[rot];
|
||||
}
|
||||
|
||||
public const float Pi = (float) Math.PI;
|
||||
public static float Min(float a, float b)
|
||||
{
|
||||
return Math.Min(a, b);
|
||||
}
|
||||
|
||||
public static float Max(float a, float b)
|
||||
{
|
||||
return Math.Max(a, b);
|
||||
}
|
||||
|
||||
public const float Pi = (float) Math.PI;
|
||||
|
||||
public static float ToDegrees(float radians)
|
||||
{
|
||||
|
||||
@@ -139,6 +139,23 @@ namespace SS14.Shared.Maths
|
||||
set => w = value;
|
||||
}
|
||||
|
||||
public float x
|
||||
{
|
||||
get => xyz.X;
|
||||
set => xyz.X = value;
|
||||
}
|
||||
|
||||
public float y
|
||||
{
|
||||
get => xyz.Y;
|
||||
set => xyz.Y = value;
|
||||
}
|
||||
|
||||
public float z
|
||||
{
|
||||
get => xyz.Z;
|
||||
set => xyz.Z = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Instance
|
||||
@@ -238,6 +255,9 @@ namespace SS14.Shared.Maths
|
||||
|
||||
#region Fields
|
||||
|
||||
private const float RadToDeg = (float)(180.0 / Math.PI);
|
||||
private const float DegToRad = (float)(Math.PI / 180.0);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the identity quaternion.
|
||||
/// </summary>
|
||||
@@ -356,6 +376,18 @@ namespace SS14.Shared.Maths
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dot
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product between two Quaternions.
|
||||
/// </summary>
|
||||
public static float Dot(Quaternion a, Quaternion b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z + a.W * b.W;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conjugate
|
||||
|
||||
/// <summary>
|
||||
@@ -530,6 +562,145 @@ namespace SS14.Shared.Maths
|
||||
|
||||
#endregion
|
||||
|
||||
#region RotateTowards
|
||||
|
||||
public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)
|
||||
{
|
||||
var num = Angle(from, to);
|
||||
if (num == 0f)
|
||||
{
|
||||
return to;
|
||||
}
|
||||
var t = Math.Min(1f, maxDegreesDelta / num);
|
||||
return Slerp(from, to, t);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Angle
|
||||
|
||||
public static float Angle(Quaternion a, Quaternion b)
|
||||
{
|
||||
var f = Dot(a, b);
|
||||
return (float) (Math.Acos(Math.Min(Math.Abs(f), 1f)) * 2f * RadToDeg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LookRotation
|
||||
|
||||
// from http://answers.unity3d.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html
|
||||
public static Quaternion LookRotation(ref Vector3 forward, ref Vector3 up)
|
||||
{
|
||||
forward = Vector3.Normalize(forward);
|
||||
Vector3 right = Vector3.Normalize(Vector3.Cross(up, forward));
|
||||
up = Vector3.Cross(forward, right);
|
||||
var m00 = right.X;
|
||||
var m01 = right.Y;
|
||||
var m02 = right.Z;
|
||||
var m10 = up.X;
|
||||
var m11 = up.Y;
|
||||
var m12 = up.Z;
|
||||
var m20 = forward.X;
|
||||
var m21 = forward.Y;
|
||||
var m22 = forward.Z;
|
||||
|
||||
var num8 = (m00 + m11) + m22;
|
||||
var quaternion = new Quaternion();
|
||||
if (num8 > 0f)
|
||||
{
|
||||
var num = (float)System.Math.Sqrt(num8 + 1f);
|
||||
quaternion.w = num * 0.5f;
|
||||
num = 0.5f / num;
|
||||
quaternion.X = (m12 - m21) * num;
|
||||
quaternion.Y = (m20 - m02) * num;
|
||||
quaternion.Z = (m01 - m10) * num;
|
||||
return quaternion;
|
||||
}
|
||||
if ((m00 >= m11) && (m00 >= m22))
|
||||
{
|
||||
var num7 = (float)System.Math.Sqrt(((1f + m00) - m11) - m22);
|
||||
var num4 = 0.5f / num7;
|
||||
quaternion.X = 0.5f * num7;
|
||||
quaternion.Y = (m01 + m10) * num4;
|
||||
quaternion.Z = (m02 + m20) * num4;
|
||||
quaternion.W = (m12 - m21) * num4;
|
||||
return quaternion;
|
||||
}
|
||||
if (m11 > m22)
|
||||
{
|
||||
var num6 = (float)System.Math.Sqrt(((1f + m11) - m00) - m22);
|
||||
var num3 = 0.5f / num6;
|
||||
quaternion.X = (m10 + m01) * num3;
|
||||
quaternion.Y = 0.5f * num6;
|
||||
quaternion.Z = (m21 + m12) * num3;
|
||||
quaternion.W = (m20 - m02) * num3;
|
||||
return quaternion;
|
||||
}
|
||||
var num5 = (float)System.Math.Sqrt(((1f + m22) - m00) - m11);
|
||||
var num2 = 0.5f / num5;
|
||||
quaternion.X = (m20 + m02) * num2;
|
||||
quaternion.Y = (m21 + m12) * num2;
|
||||
quaternion.Z = 0.5f * num5;
|
||||
quaternion.W = (m01 - m10) * num2;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Euler Angles
|
||||
|
||||
// from http://stackoverflow.com/questions/12088610/conversion-between-euler-quaternion-like-in-unity3d-engine
|
||||
public static Vector3 ToEulerRad(Quaternion rotation)
|
||||
{
|
||||
float sqw = rotation.w * rotation.w;
|
||||
float sqx = rotation.x * rotation.x;
|
||||
float sqy = rotation.y * rotation.y;
|
||||
float sqz = rotation.z * rotation.z;
|
||||
float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
|
||||
float test = rotation.x * rotation.w - rotation.y * rotation.z;
|
||||
Vector3 v;
|
||||
|
||||
if (test > 0.4995f * unit)
|
||||
{ // singularity at north pole
|
||||
v.Y = (float) (2f * Math.Atan2(rotation.y, rotation.x));
|
||||
v.X = (float) (Math.PI / 2);
|
||||
v.Z = 0;
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
if (test < -0.4995f * unit)
|
||||
{ // singularity at south pole
|
||||
v.Y = (float) (-2f * Math.Atan2(rotation.y, rotation.x));
|
||||
v.X = (float) (-Math.PI / 2);
|
||||
v.Z = 0;
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
Quaternion q = new Quaternion(rotation.w, rotation.z, rotation.x, rotation.y);
|
||||
v.Y = (float)System.Math.Atan2(2f * q.x * q.w + 2f * q.y * q.z, 1 - 2f * (q.z * q.z + q.w * q.w)); // Yaw
|
||||
v.X = (float)System.Math.Asin(2f * (q.x * q.z - q.w * q.y)); // Pitch
|
||||
v.Z = (float)System.Math.Atan2(2f * q.x * q.y + 2f * q.z * q.w, 1 - 2f * (q.y * q.y + q.z * q.z)); // Roll
|
||||
return NormalizeAngles(v * RadToDeg);
|
||||
}
|
||||
|
||||
private static Vector3 NormalizeAngles(Vector3 angles)
|
||||
{
|
||||
angles.X = NormalizeAngle(angles.X);
|
||||
angles.Y = NormalizeAngle(angles.Y);
|
||||
angles.Z = NormalizeAngle(angles.Z);
|
||||
return angles;
|
||||
}
|
||||
|
||||
private static float NormalizeAngle(float angle)
|
||||
{
|
||||
while (angle > 360)
|
||||
angle -= 360;
|
||||
while (angle < 0)
|
||||
angle += 360;
|
||||
return angle;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
170
SS14.Shared/Maths/Ray.cs
Normal file
170
SS14.Shared/Maths/Ray.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
|
||||
namespace SS14.Shared.Maths
|
||||
{
|
||||
/// <summary>
|
||||
/// A representation of a 2D ray.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct Ray : IEquatable<Ray>
|
||||
{
|
||||
private readonly Vector2 _position;
|
||||
private readonly Vector2 _direction;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the starting point of the ray.
|
||||
/// </summary>
|
||||
public Vector2 Position => _position;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the direction the ray is pointing.
|
||||
/// </summary>
|
||||
public Vector2 Direction => _direction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a Ray.
|
||||
/// </summary>
|
||||
/// <param name="position">Starting position of the ray.</param>
|
||||
/// <param name="direction">Unit direction vector that the ray is pointing.</param>
|
||||
public Ray(Vector2 position, Vector2 direction)
|
||||
{
|
||||
_position = position;
|
||||
_direction = direction;
|
||||
}
|
||||
|
||||
#region Intersect Tests
|
||||
|
||||
public bool Intersects(Box2 box, out float distance, out Vector2 hitPos)
|
||||
{
|
||||
hitPos = Vector2.Zero;
|
||||
distance = 0;
|
||||
|
||||
float tmin = 0.0f; // set to -FLT_MAX to get first hit on line
|
||||
float tmax = float.MaxValue; // set to max distance ray can travel (for segment)
|
||||
const float epsilon = 1.0E-07f;
|
||||
|
||||
// X axis slab
|
||||
{
|
||||
if (Math.Abs(_direction.X) < epsilon)
|
||||
{
|
||||
// ray is parallel to this slab, it will never hit unless ray is inside box
|
||||
if (_position.X < FloatMath.Min(box.Left, box.Right) || _position.X > FloatMath.Max(box.Left, box.Right))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate intersection t value of ray with near and far plane of slab
|
||||
var ood = 1.0f / _direction.X;
|
||||
var t1 = (FloatMath.Min(box.Left, box.Right) - _position.X) * ood;
|
||||
var t2 = (FloatMath.Max(box.Left, box.Right) - _position.X) * ood;
|
||||
|
||||
// Make t1 be the intersection with near plane, t2 with far plane
|
||||
if (t1 > t2)
|
||||
MathHelper.Swap(ref t1, ref t2);
|
||||
|
||||
// Compute the intersection of slab intersection intervals
|
||||
tmin = FloatMath.Max(t1, tmin);
|
||||
tmax = FloatMath.Min(t2, tmax); // Is this Min (SE) or Max(Textbook)
|
||||
|
||||
// Exit with no collision as soon as slab intersection becomes empty
|
||||
if (tmin > tmax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Y axis slab
|
||||
{
|
||||
if (Math.Abs(_direction.Y) < epsilon)
|
||||
{
|
||||
// ray is parallel to this slab, it will never hit unless ray is inside box
|
||||
if (_position.Y < FloatMath.Min(box.Top, box.Bottom) || _position.Y > FloatMath.Max(box.Top, box.Bottom))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate intersection t value of ray with near and far plane of slab
|
||||
var ood = 1.0f / _direction.Y;
|
||||
var t1 = (FloatMath.Min(box.Top, box.Bottom) - _position.Y) * ood;
|
||||
var t2 = (FloatMath.Max(box.Top, box.Bottom) - _position.Y) * ood;
|
||||
|
||||
// Make t1 be the intersection with near plane, t2 with far plane
|
||||
if (t1 > t2)
|
||||
MathHelper.Swap(ref t1, ref t2);
|
||||
|
||||
// Compute the intersection of slab intersection intervals
|
||||
tmin = FloatMath.Max(t1, tmin);
|
||||
tmax = FloatMath.Min(t2, tmax); // Is this Min (SE) or Max(Textbook)
|
||||
|
||||
// Exit with no collision as soon as slab intersection becomes empty
|
||||
if (tmin > tmax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Ray intersects all slabs. Return point and intersection t value
|
||||
hitPos = _position + _direction * tmin;
|
||||
distance = tmin;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this Ray and another Ray are equivalent.
|
||||
/// </summary>
|
||||
/// <param name="other">Ray to compare to.</param>
|
||||
public bool Equals(Ray other)
|
||||
{
|
||||
return _position.Equals(other._position) && _direction.Equals(other._direction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this ray and another object is equivalent.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to compare to.</param>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
return obj is Ray ray && Equals(ray);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the hash code of this Ray.
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (_position.GetHashCode() * 397) ^ _direction.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if two instances of Ray are equal.
|
||||
/// </summary>
|
||||
/// <param name="a">Ray on the left side of the operator.</param>
|
||||
/// <param name="b">Ray on the right side of the operator.</param>
|
||||
public static bool operator ==(Ray a, Ray b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if two instances of Ray are not equal.
|
||||
/// </summary>
|
||||
/// <param name="a">Ray on the left side of the operator.</param>
|
||||
/// <param name="b">Ray on the right side of the operator.</param>
|
||||
public static bool operator !=(Ray a, Ray b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,16 @@ namespace SS14.Shared.Maths
|
||||
/// </summary>
|
||||
public static readonly Vector2 One = new Vector2(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// A unit vector pointing in the +X direction.
|
||||
/// </summary>
|
||||
public static readonly Vector2 UnitX = new Vector2(1, 0);
|
||||
|
||||
/// <summary>
|
||||
/// A unit vector pointing in the +Y direction.
|
||||
/// </summary>
|
||||
public static readonly Vector2 UnitY = new Vector2(0, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Construct a vector from its coordinates.
|
||||
/// </summary>
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace SS14.Shared.Network
|
||||
/// <inheritdoc />
|
||||
public string RemoteAddress => _connection.RemoteEndPoint.Address.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Exposes the lidgren connection.
|
||||
/// </summary>
|
||||
public NetConnection Connection => _connection;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a NetChannel.
|
||||
/// </summary>
|
||||
|
||||
@@ -441,15 +441,12 @@ namespace SS14.Shared.Network
|
||||
{
|
||||
if (_netPeer == null)
|
||||
return;
|
||||
|
||||
var packet = BuildMessage(message);
|
||||
var connection = ChanToCon(recipient);
|
||||
_netPeer.SendMessage(packet, connection, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
private NetConnection ChanToCon(INetChannel channel)
|
||||
{
|
||||
return _channels.FirstOrDefault(x => x.Value == channel).Key;
|
||||
if(!(recipient is NetChannel channel))
|
||||
throw new ArgumentException($"Not of type {typeof(NetChannel).FullName}", nameof(recipient));
|
||||
|
||||
var packet = BuildMessage(message);
|
||||
_netPeer.SendMessage(packet, channel.Connection, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -198,6 +198,71 @@ namespace SS14.Shared.Physics
|
||||
b.RemovePoint(point);
|
||||
}
|
||||
|
||||
public RayCastResults IntersectRay(Ray ray, float maxLength = 50)
|
||||
{
|
||||
var closestResults = new RayCastResults(float.PositiveInfinity, Vector2.Zero, null);
|
||||
var minDist = float.PositiveInfinity;
|
||||
var localBounds = new Box2(0, BucketSize, BucketSize, 0);
|
||||
|
||||
// for each bucket index
|
||||
foreach (var kvIndices in _bucketIndex)
|
||||
{
|
||||
var worldBounds = localBounds.Translated(kvIndices.Key * BucketSize);
|
||||
|
||||
// check if ray intersects the bucket AABB
|
||||
if (ray.Intersects(worldBounds, out var dist, out _))
|
||||
{
|
||||
// bucket is too far away
|
||||
if(dist > maxLength)
|
||||
continue;
|
||||
|
||||
// get the object it intersected in the bucket
|
||||
var bucket = _buckets[kvIndices.Value];
|
||||
if (TryGetClosestIntersect(ray, bucket, out var results))
|
||||
{
|
||||
if (results.Distance < minDist)
|
||||
{
|
||||
minDist = results.Distance;
|
||||
closestResults = results;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestResults;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the closest object, inside a bucket, to the ray origin that was intersected (if any).
|
||||
/// </summary>
|
||||
private static bool TryGetClosestIntersect(Ray ray, CollidableBucket bucket, out RayCastResults results)
|
||||
{
|
||||
IEntity entity = null;
|
||||
var hitPosition = Vector2.Zero;
|
||||
var minDist = float.PositiveInfinity;
|
||||
|
||||
foreach (var collidablePoint in bucket.GetPoints()) // *goes to kitchen to freshen up his drink...*
|
||||
{
|
||||
var worldAABB = collidablePoint.ParentAABB.Collidable.WorldAABB;
|
||||
|
||||
if (ray.Intersects(worldAABB, out var dist, out var hitPos) && !(dist > minDist))
|
||||
{
|
||||
minDist = dist;
|
||||
hitPosition = hitPos;
|
||||
entity = collidablePoint.ParentAABB.Collidable.Owner;
|
||||
}
|
||||
}
|
||||
|
||||
if (minDist < float.PositiveInfinity)
|
||||
{
|
||||
results = new RayCastResults(minDist, hitPosition, entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
results = default(RayCastResults);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bucket given a point coordinate
|
||||
/// </summary>
|
||||
|
||||
21
SS14.Shared/Physics/RayCastResults.cs
Normal file
21
SS14.Shared/Physics/RayCastResults.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace SS14.Shared.Physics
|
||||
{
|
||||
public struct RayCastResults
|
||||
{
|
||||
public bool HitObject => Distance < float.PositiveInfinity;
|
||||
|
||||
public IEntity HitEntity { get; }
|
||||
public Vector2 HitPos { get; }
|
||||
public float Distance { get; }
|
||||
|
||||
public RayCastResults(float distance, Vector2 hitPos, IEntity hitEntity)
|
||||
{
|
||||
Distance = distance;
|
||||
HitPos = hitPos;
|
||||
HitEntity = hitEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,7 @@
|
||||
<Compile Include="Maths\Matrix3.cs" />
|
||||
<Compile Include="Maths\Matrix4.cs" />
|
||||
<Compile Include="Maths\Quaternion.cs" />
|
||||
<Compile Include="Maths\Ray.cs" />
|
||||
<Compile Include="Maths\Vector2.cs" />
|
||||
<Compile Include="Maths\Vector2i.cs" />
|
||||
<Compile Include="Maths\Vector2u.cs" />
|
||||
@@ -237,6 +238,7 @@
|
||||
<Compile Include="Physics\CollisionManager.cs" />
|
||||
<Compile Include="Enums\PlacementInformation.cs" />
|
||||
<Compile Include="Input\KeyFunctions.cs" />
|
||||
<Compile Include="Physics\RayCastResults.cs" />
|
||||
<Compile Include="Players\PlayerIndex.cs" />
|
||||
<Compile Include="Utility\QuadTree.cs" />
|
||||
<Compile Include="Reflection\ReflectAttribute.cs" />
|
||||
|
||||
@@ -49,15 +49,25 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.8.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\Moq.4.8.2\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>$(SolutionDir)packages\NUnit.3.7.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.ValueTuple">
|
||||
<HintPath>$(SolutionDir)packages\System.ValueTuple.4.3.1\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="YamlDotNet, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\YamlDotNet.4.3.0\lib\net45\YamlDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -81,8 +91,10 @@
|
||||
<Compile Include="Shared\IoC\IoCManager_Test.cs" />
|
||||
<Compile Include="Shared\Maths\Angle_Test.cs" />
|
||||
<Compile Include="Shared\Maths\Matrix3_Test.cs" />
|
||||
<Compile Include="Shared\Maths\Ray_Test.cs" />
|
||||
<Compile Include="Shared\Maths\Vector2_Test.cs" />
|
||||
<Compile Include="Shared\Maths\Direction_Test.cs" />
|
||||
<Compile Include="Shared\Physics\CollisionManager_Test.cs" />
|
||||
<Compile Include="Shared\Prototypes\PrototypeManager_Test.cs" />
|
||||
<Compile Include="Shared\Reflection\ReflectionManager_Test.cs" />
|
||||
<Compile Include="Shared\Serialization\NetSerializableAttribute_Test.cs" />
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using SS14.Client.Interfaces.GameObjects;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.Prototypes;
|
||||
|
||||
namespace SS14.UnitTesting.Server.GameObjects
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(ServerEntityManager))]
|
||||
class ServerEntityManager_Test : SS14UnitTest
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Server;
|
||||
|
||||
private IServerEntityManager EntityManager;
|
||||
|
||||
const string PROTOTYPES = @"
|
||||
- type: entity
|
||||
name: dummyPoint
|
||||
id: dummyPoint
|
||||
components:
|
||||
- type: Transform
|
||||
|
||||
- type: entity
|
||||
name: dummyAABB
|
||||
id: dummyAABB
|
||||
components:
|
||||
- type: Transform
|
||||
- type: BoundingBox
|
||||
";
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
EntityManager = IoCManager.Resolve<IServerEntityManager>();
|
||||
|
||||
var manager = IoCManager.Resolve<IPrototypeManager>();
|
||||
manager.LoadFromStream(new StringReader(PROTOTYPES));
|
||||
manager.Resync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEntityInRangePointTest()
|
||||
{
|
||||
// Arrange
|
||||
var baseEnt = EntityManager.SpawnEntity("dummyPoint");
|
||||
var inRangeEnt = EntityManager.SpawnEntity("dummyPoint");
|
||||
inRangeEnt.GetComponent<IServerTransformComponent>().WorldPosition = new Vector2(-2, -2);
|
||||
|
||||
// Act
|
||||
var results = EntityManager.GetEntitiesInRange(baseEnt, 4.00f);
|
||||
|
||||
// Cleanup
|
||||
var list = results.ToList();
|
||||
EntityManager.FlushEntities();
|
||||
|
||||
// Assert
|
||||
Assert.That(list.Count, Is.EqualTo(2), list.Count.ToString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEntityInRangeAABBTest()
|
||||
{
|
||||
// Arrange
|
||||
var baseEnt = EntityManager.SpawnEntity("dummyAABB");
|
||||
var inRangeEnt = EntityManager.SpawnEntity("dummyAABB");
|
||||
inRangeEnt.GetComponent<IServerTransformComponent>().WorldPosition = new Vector2(-2, -2);
|
||||
|
||||
// Act
|
||||
var results = EntityManager.GetEntitiesInRange(baseEnt, 4.00f);
|
||||
|
||||
// Cleanup
|
||||
var list = results.ToList();
|
||||
EntityManager.FlushEntities();
|
||||
|
||||
// Assert
|
||||
Assert.That(list.Count, Is.EqualTo(2), list.Count.ToString);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
SS14.UnitTesting/Shared/Maths/Ray_Test.cs
Normal file
24
SS14.UnitTesting/Shared/Maths/Ray_Test.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using NUnit.Framework;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace SS14.UnitTesting.Shared.Maths
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(Ray))]
|
||||
class Ray_Test
|
||||
{
|
||||
[Test]
|
||||
public void RayIntersectsBoxTest()
|
||||
{
|
||||
var box = new Box2(new Vector2(5, 5), new Vector2(10, -5));
|
||||
var ray = new Ray(new Vector2(0, 1), Vector2.UnitX);
|
||||
|
||||
var result = ray.Intersects(box, out var dist, out var hitPos);
|
||||
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(dist, Is.EqualTo(5));
|
||||
Assert.That(hitPos.X, Is.EqualTo(5));
|
||||
Assert.That(hitPos.Y, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
35
SS14.UnitTesting/Shared/Physics/CollisionManager_Test.cs
Normal file
35
SS14.UnitTesting/Shared/Physics/CollisionManager_Test.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using SS14.Shared.Interfaces.Physics;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.Physics;
|
||||
|
||||
namespace SS14.UnitTesting.Shared.Physics
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(CollisionManager))]
|
||||
class CollisionManager_Test
|
||||
{
|
||||
[Test]
|
||||
public void RayCastTest()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(new Vector2(5, 5), new Vector2(10, -5));
|
||||
var ray = new Ray(new Vector2(0, 1), Vector2.UnitX);
|
||||
var manager = new CollisionManager();
|
||||
|
||||
var mock = new Mock<ICollidable>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
manager.AddCollidable(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.IntersectRay(ray);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.HitObject, Is.True);
|
||||
Assert.That(result.Distance, Is.EqualTo(5));
|
||||
Assert.That(result.HitPos.X, Is.EqualTo(5));
|
||||
Assert.That(result.HitPos.Y, Is.EqualTo(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="4.2.1" targetFramework="net451" />
|
||||
<package id="Moq" version="4.8.2" targetFramework="net451" />
|
||||
<package id="NUnit" version="3.7.1" targetFramework="net451" />
|
||||
<package id="NUnit.ConsoleRunner" version="3.7.0" targetFramework="net451" />
|
||||
<package id="NUnit3TestAdapter" version="3.8.0" targetFramework="net451" />
|
||||
<package id="System.ValueTuple" version="4.3.1" targetFramework="net451" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net451" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net451" />
|
||||
<package id="YamlDotNet" version="4.3.0" targetFramework="net451" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user