Przeglądaj źródła

Added CharacterController to C#

BearishSun 9 lat temu
rodzic
commit
5e9ec801e6

+ 3 - 3
BansheeCore/Include/BsCharacterController.h

@@ -185,10 +185,10 @@ namespace BansheeEngine
 		 */
 		virtual void setSlopeLimit(Radian value) = 0;
 
-		/** Sets the layer that control swhat can the controller collide with. */
+		/** Sets the layer that controls what can the controller collide with. */
 		virtual void setLayer(UINT64 layer) { mLayer = layer; }
 
-		/** Gets the layer that control swhat can the controller collide with. */
+		/** Gets the layer that controls what can the controller collide with. */
 		virtual UINT64 getLayer() const { return mLayer; }
 
 		/** Creates a new character controller. */
@@ -246,7 +246,7 @@ namespace BansheeEngine
 
 		/** 
 		 * Represents minimum distance that the character will move during a call to move(). This is used to stop the
-		 * recursive motion algorithm when the remaining distance is too small
+		 * recursive motion algorithm when the remaining distance is too small.
 		 */
 		float minMoveDistance = 0.0f;
 

+ 3 - 3
BansheeCore/Include/BsGameObject.h

@@ -16,9 +16,9 @@ namespace BansheeEngine
 	/** Flags used for notifying child scene object and components when a transform has been changed. */
 	enum TransformChangedFlags
 	{
-		TCF_None = 0x00,
-		TCF_Transform = 0x01,
-		TCF_Parent = 0x02
+		TCF_None = 0x00, /**< Component will not be notified about any events relating to the transform. */
+		TCF_Transform = 0x01, /**< Component will be notified when the its position, rotation or scale has changed. */
+		TCF_Parent = 0x02 /**< Component will be notified when its parent changes. */
 	};
 
 	/** @endcond */

+ 2 - 0
BansheeCore/Source/BsCRigidbody.cpp

@@ -14,6 +14,8 @@ namespace BansheeEngine
 		: Component(parent)
 	{
 		setName("Rigidbody");
+
+		mNotifyFlags = (TransformChangedFlags)(TCF_Parent | TCF_Transform);
 	}
 
 	void CRigidbody::move(const Vector3& position)

+ 12 - 0
MBansheeEngine/Component.cs

@@ -21,6 +21,9 @@ namespace BansheeEngine
     ///                    current frame unless specified otherwise in a call to Destroy. 
     /// void OnReset() - Called when script assemblies have been refreshed or when the component is initialized. During
     ///                  initialization it is called after OnInitialize but before OnEnable. Only relevant in editor.
+    /// void OnTransformChanged(TransformChangedFlags) - Called when the transform of the owning scene object changes.
+    ///                                                  When and if this gets triggered depends on 
+    ///                                                  <see cref="NotifyFlags"/>.
     ///
     /// You can also make these callbacks trigger when the game is stopped/paused by using the <see cref="RunInEditor"/>
     /// attribute on the component.
@@ -39,6 +42,15 @@ namespace BansheeEngine
             get { return Internal_GetSceneObject(mCachedPtr); }
         }
 
+        /// <summary>
+        /// Determines in which situations will OnTransformChanged be triggered.
+        /// </summary>
+        protected TransformChangedFlags NotifyFlags
+        {
+            set { /* TODO */}
+            get { return TransformChangedFlags.None; /* TODO */ }
+        }
+
         /// <summary>
         /// Destroys the component, removing it from its scene object and stopping component updates.
         /// </summary>

+ 10 - 0
MBansheeEngine/GameObject.cs

@@ -25,10 +25,20 @@ namespace BansheeEngine
     /// <summary>
     /// Flags used for notifying child scene object and components when a transform has been changed.
     /// </summary>
+    [Flags]
     public enum TransformChangedFlags
     {
+        /// <summary>
+        /// Component will not be notified about any events relating to the transform.
+        /// </summary>
         None = 0x00,
+        /// <summary>
+        /// Component will be notified when the its position, rotation or scale has changed.
+        /// </summary>
         Transform = 0x01,
+        /// <summary>
+        /// Component will be notified when its parent changes.
+        /// </summary>
         Parent = 0x02
     }
 }

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -117,6 +117,7 @@
     <Compile Include="Physics\MeshCollider.cs" />
     <Compile Include="Physics\NativeBoxCollider.cs" />
     <Compile Include="Physics\NativeCapsuleCollider.cs" />
+    <Compile Include="Physics\NativeCharacterController.cs" />
     <Compile Include="Physics\NativeCollider.cs" />
     <Compile Include="Physics\NativeMeshCollider.cs" />
     <Compile Include="Physics\NativePlaneCollider.cs" />

+ 324 - 6
MBansheeEngine/Physics/CharacterController.cs

@@ -1,5 +1,7 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+using System;
 using System.Runtime.InteropServices;
 
 namespace BansheeEngine
@@ -11,10 +13,327 @@ namespace BansheeEngine
     /// </summary>
     public class CharacterController : Component
     {
+        internal NativeCharacterController native;
+
         [SerializeField]
         internal SerializableData serializableData = new SerializableData();
 
-        // TODO
+        /// <summary>
+        /// Triggered when the controller hits a collider.
+        /// </summary>
+        public event Action<ControllerColliderCollision> OnColliderHit;
+
+        /// <summary>
+        /// Triggered when the controller hits another character controller.
+        /// </summary>
+        public event Action<ControllerControllerCollision> OnControllerHit;
+
+        /// <summary>
+        /// Position of the bottom of the controller. Position takes contact offset into account. Changing this value will 
+        /// teleport the character to the location. Use <see cref="Move"/> for movement that includes physics.
+        /// </summary>
+        public Vector3 FootPosition
+        {
+            get
+            {
+                if (native != null)
+                    return native.FootPosition;
+
+                return Vector3.Zero;
+            }
+            set
+            {
+                if (native != null)
+                {
+                    native.FootPosition = value;
+                    UpdatePositionFromController();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Radius of the controller capsule.
+        /// </summary>
+        public float Radius
+        {
+            get { return serializableData.radius; }
+            set
+            {
+                serializableData.radius = value;
+
+                if(native != null)
+                    UpdateDimensions();
+            }
+        }
+
+        /// <summary>
+        /// Height between the centers of the two spheres of the controller capsule.
+        /// </summary>
+        public float Height
+        {
+            get { return serializableData.height; }
+            set
+            {
+                serializableData.height = value;
+
+                if(native != null)
+                    UpdateDimensions();
+            }
+        }
+
+        /// <summary>
+        /// Up direction of capsule. Determines capsule orientation.
+        /// </summary>
+        public Vector3 Up
+        {
+            get { return serializableData.up; }
+            set
+            {
+                serializableData.up = value;
+
+                if (native != null)
+                    native.Up = value;
+            }
+        }
+
+        /// <summary>
+        /// Controls what happens when character encounters a height higher than its step offset. 
+        /// </summary>
+        public CharacterClimbingMode ClimbingMode
+        {
+            get { return serializableData.climbingMode; }
+            set
+            {
+                serializableData.climbingMode = value;
+
+                if (native != null)
+                    native.ClimbingMode = value;
+            }
+        }
+
+        /// <summary>
+        /// Controls what happens when character encounters a slope higher than its slope offset. 
+        /// </summary>
+        public CharacterNonWalkableMode NonWalkableMode
+        {
+            get { return serializableData.nonWalkableMode; }
+            set
+            {
+                serializableData.nonWalkableMode = value;
+
+                if (native != null)
+                    native.NonWalkableMode = value;
+            }
+        }
+
+        /// <summary>
+        /// Represents minimum distance that the character will move during a call to <see cref="Move"/>. This is used to
+        /// stop the recursive motion algorithm when the remaining distance is too small.
+        /// </summary>
+        public float MinMoveDistance
+        {
+            get { return serializableData.minMoveDistance; }
+            set
+            {
+                serializableData.minMoveDistance = value;
+
+                if (native != null)
+                    native.MinMoveDistance = value;
+            }
+        }
+
+        /// <summary>
+        /// Contact offset specifies a skin around the object within which contacts will be generated. It should be a small
+        /// positive non-zero value.
+        /// </summary>
+        public float ContactOffset
+        {
+            get { return serializableData.contactOffset; }
+            set
+            {
+                serializableData.contactOffset = value;
+
+                if (native != null)
+                    native.ContactOffset = value;
+            }
+        }
+
+        /// <summary>
+        /// Controls which obstacles will the character be able to automatically step over without being stopped. This is 
+        /// the height of the maximum obstacle that will be stepped over (with exceptions, <see cref="ClimbingMode"/>).
+        /// </summary>
+        public float StepOffset
+        {
+            get { return serializableData.stepOffset; }
+            set
+            {
+                serializableData.stepOffset = value;
+
+                if (native != null)
+                    native.StepOffset = value;
+            }
+        }
+
+        /// <summary>
+        /// Controls which slopes should the character consider too steep and won't be able to move over. 
+        /// <see cref="NonWalkableMode"/> for more information.
+        /// </summary>
+        public Radian SlopeLimit
+        {
+            get { return serializableData.slopeLimit; }
+            set
+            {
+                serializableData.slopeLimit = value;
+
+                if (native != null)
+                    native.SlopeLimit = value;
+            }
+        }
+
+        /// <summary>
+        /// Determines what can the controller collide with. Objects that are allowed to collide with this object must have
+        /// the same bits set in their own layers.
+        /// </summary>
+        public ulong Layer
+        {
+            get { return serializableData.layer; }
+            set
+            {
+                serializableData.layer = value;
+
+                if (native != null)
+                    native.Layer = value;
+            }
+        }
+
+        /// <summary>
+        /// Moves the controller in the specified direction by the specified amount, while interacting with surrounding
+        /// geometry.Returns flags signaling where collision occurred after the movement.
+        /// 
+        /// Does not account for gravity, you must apply it manually.
+        /// </summary>
+        /// <param name="position">Position to move the controller to, in world space.</param>
+        public void Move(Vector3 position)
+        {
+            if (native == null)
+                return;
+
+            native.Move(position);
+            UpdatePositionFromController();
+        }
+
+        /// <summary>
+        /// Triggered when the controller hits a collider.
+        /// </summary>
+        /// <param name="data">Data about the collision.</param>
+        internal void DoOnColliderHit(ControllerColliderCollision data)
+        {
+            if (OnColliderHit != null)
+                OnColliderHit(data);
+        }
+
+        /// <summary>
+        /// Triggered when the controller hits another character controller.
+        /// </summary>
+        /// <param name="data">Data about the collision.</param>
+        internal void DoOnControllerHit(ControllerControllerCollision data)
+        {
+            if (OnControllerHit != null)
+                OnControllerHit(data);
+        }
+
+        private void OnInitialize()
+        {
+            NotifyFlags = TransformChangedFlags.Transform;
+        }
+
+        private void OnReset()
+        {
+            RestoreNative();
+        }
+
+        private void OnEnable()
+        {
+            if (native == null)
+                RestoreNative();
+        }
+
+        private void OnDisable()
+        {
+            DestroyNative();
+        }
+
+        private void OnDestroy()
+        {
+            DestroyNative();
+        }
+
+        private void OnTransformChanged(TransformChangedFlags flags)
+        {
+            if (!SceneObject.Active || native == null)
+                return;
+
+            native.Position = SceneObject.Position;
+        }
+
+        /// <summary>
+        /// Updates the position by copying it from the controller to the component's scene object.
+        /// </summary>
+        private void UpdatePositionFromController()
+        {
+            NotifyFlags = 0;
+            SceneObject.Position = native.Position;
+            NotifyFlags = TransformChangedFlags.Transform;
+        }
+
+        /// <summary>
+        /// Updates the dimensions of the controller by taking account scale of the parent scene object.
+        /// </summary>
+        private void UpdateDimensions()
+        {
+            Vector3 scale = SceneObject.Scale;
+            float height = serializableData.height * MathEx.Abs(scale.y);
+            float radius = serializableData.radius * MathEx.Abs(MathEx.Max(scale.x, scale.z));
+
+            native.Height = height;
+            native.Radius = radius;
+        }
+
+        /// <summary>
+        /// Restores the internal character controller representation and initializes it with data stored by the component.
+        /// </summary>
+        private void RestoreNative()
+        {
+            ScriptCharacterControllerData initData = new ScriptCharacterControllerData();
+            initData.position = SceneObject.Position;
+            initData.contactOffset = serializableData.contactOffset;
+            initData.stepOffset = serializableData.stepOffset;
+            initData.slopeLimit = serializableData.slopeLimit;
+            initData.minMoveDistance = serializableData.minMoveDistance;
+            initData.height = serializableData.height;
+            initData.radius = serializableData.radius;
+            initData.up = serializableData.up;
+            initData.climbingMode = serializableData.climbingMode;
+            initData.nonWalkableMode = serializableData.nonWalkableMode;
+
+            native = new NativeCharacterController(initData);
+            native.Component = this;
+            native.Layer = serializableData.layer;
+
+            UpdateDimensions();
+        }
+
+        /// <summary>
+        /// Destroys the internal character controller representation.
+        /// </summary>
+        private void DestroyNative()
+        {
+            if (native != null)
+            {
+                native.Destroy();
+                native = null;
+            }
+        }
 
         /// <summary>
         /// Holds all data the character controller component needs to persist through serialization.
@@ -22,7 +341,6 @@ namespace BansheeEngine
         [SerializeObject]
         internal class SerializableData
         {
-            public Vector3 position;
             public float contactOffset = 0.1f;
             public float stepOffset = 0.5f;
             public Radian slopeLimit = new Degree(45.0f);
@@ -32,6 +350,7 @@ namespace BansheeEngine
             public Vector3 up = Vector3.YAxis;
             public CharacterClimbingMode climbingMode = CharacterClimbingMode.Normal;
             public CharacterNonWalkableMode nonWalkableMode = CharacterNonWalkableMode.Prevent;
+            public ulong layer = 1;
         }
     }
 
@@ -90,7 +409,7 @@ namespace BansheeEngine
     /// Used for passing CharacterController initialization data between native and managed code.
     /// </summary>
     [StructLayout(LayoutKind.Sequential)]
-    internal struct ScriptCharacterControllerData
+    internal struct ScriptCharacterControllerData // Note: Must match C++ struct CHAR_CONTROLLER_DESC
     {
         public Vector3 position;
         public float contactOffset;
@@ -113,10 +432,9 @@ namespace BansheeEngine
         public Vector3 normal;
         public Vector3 motionDir;
         public float motionAmount;
-        public Collider collider;
+        public NativeCollider collider;
         public int triangleIndex;
-        public CharacterController controller;
-        public bool isControllerCollision;
+        public NativeCharacterController controller;
     }
 
     /// <summary>

+ 7 - 2
MBansheeEngine/Physics/Collider.cs

@@ -230,7 +230,7 @@ namespace BansheeEngine
 			    NativeRigidbody nativeRigidbody = null;
 
 			    if (rigidbody != null)
-                    nativeRigidbody = rigidbody.Native;
+                    nativeRigidbody = rigidbody.native;
 
 		        native.Rigidbody = nativeRigidbody;;
 
@@ -245,7 +245,7 @@ namespace BansheeEngine
         /// <summary>
         /// Triggered when the internal collider begins touching another object.
         /// </summary>
-        /// <param name="data">Data about a collision.</param>
+        /// <param name="data">Data about the collision.</param>
         internal void DoOnCollisionBegin(CollisionData data)
         {
             if (OnCollisionBegin != null)
@@ -373,6 +373,11 @@ namespace BansheeEngine
             native.CollisionReportMode = mode;
         }
 
+        private void OnInitialize()
+        {
+            NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
+        }
+
         private void OnReset()
         {
             RestoreNative();

+ 180 - 0
MBansheeEngine/Physics/NativeCharacterController.cs

@@ -0,0 +1,180 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Wrapper around the native CharacterController class.
+    /// <see cref="CharacterController"/>
+    /// </summary>
+    internal class NativeCharacterController : ScriptObject
+    {
+        private CharacterController component;
+
+        /// <summary>
+        /// Component that owns the native character controller object.
+        /// </summary>
+        public CharacterController Component
+        {
+            get { return component; }
+            set { component = value; }
+        }
+
+        public Vector3 Position
+        {
+            get { Vector3 pos; Internal_GetPosition(mCachedPtr, out pos); return pos; }
+            set { Internal_SetPosition(mCachedPtr, ref value); }
+        }
+
+        public Vector3 FootPosition
+        {
+            get { Vector3 pos; Internal_GetFootPosition(mCachedPtr, out pos); return pos; }
+            set { Internal_SetFootPosition(mCachedPtr, ref value); }
+        }
+
+        public float Radius
+        {
+            set { Internal_SetRadius(mCachedPtr, value); }
+        }
+
+        public float Height
+        {
+            set { Internal_SetHeight(mCachedPtr, value); }
+        }
+
+        public Vector3 Up
+        {
+            set { Internal_SetUp(mCachedPtr, ref value); }
+        }
+
+        public CharacterClimbingMode ClimbingMode
+        {
+            set { Internal_SetClimbingMode(mCachedPtr, value); }
+        }
+
+        public CharacterNonWalkableMode NonWalkableMode
+        {
+            set { Internal_SetNonWalkableMode(mCachedPtr, value); }
+        }
+
+        public float MinMoveDistance
+        {
+            set { Internal_SetMinMoveDistance(mCachedPtr, value); }
+        }
+
+        public float ContactOffset
+        {
+            set { Internal_SetContactOffset(mCachedPtr, value); }
+        }
+
+        public float StepOffset
+        {
+            set { Internal_SetStepOffset(mCachedPtr, value); }
+        }
+
+        public Radian SlopeLimit
+        {
+            set { Internal_SetSlopeLimit(mCachedPtr, value.Radians); }
+        }
+
+        public ulong Layer
+        {
+            set { Internal_SetLayer(mCachedPtr, value); }
+        }
+
+        public NativeCharacterController(ScriptCharacterControllerData initData)
+        {
+            Internal_CreateInstance(this, ref initData);
+        }
+
+        public void Destroy()
+        {
+            Internal_Destroy(mCachedPtr);
+        }
+
+        public void Move(Vector3 position)
+        {
+            Internal_Move(mCachedPtr, ref position);
+        }
+
+        private void Internal_DoOnColliderHit(ScriptControllerCollision scriptCollisionData)
+        {
+            ControllerColliderCollision collisionData = new ControllerColliderCollision();
+            collisionData.position = scriptCollisionData.position;
+            collisionData.normal = scriptCollisionData.normal;
+            collisionData.motionDir = scriptCollisionData.motionDir;
+            collisionData.motionAmount = scriptCollisionData.motionAmount;
+            collisionData.triangleIndex = scriptCollisionData.triangleIndex;
+            collisionData.collider = scriptCollisionData.collider.Component;
+
+            Component.DoOnColliderHit(collisionData);
+        }
+
+        private void Internal_DoOnControllerHit(ScriptControllerCollision scriptCollisionData)
+        {
+            ControllerControllerCollision collisionData = new ControllerControllerCollision();
+            collisionData.position = scriptCollisionData.position;
+            collisionData.normal = scriptCollisionData.normal;
+            collisionData.motionDir = scriptCollisionData.motionDir;
+            collisionData.motionAmount = scriptCollisionData.motionAmount;
+            collisionData.controller = scriptCollisionData.controller.Component;
+
+            Component.DoOnControllerHit(collisionData);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(NativeCharacterController instance, 
+            ref ScriptCharacterControllerData initData);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Destroy(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_Move(IntPtr thisPtr, ref Vector3 position);
+        
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetPosition(IntPtr thisPtr, out Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetPosition(IntPtr thisPtr, ref Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_GetFootPosition(IntPtr thisPtr, out Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetFootPosition(IntPtr thisPtr, ref Vector3 position);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetRadius(IntPtr thisPtr, float radius);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetHeight(IntPtr thisPtr, float height);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetUp(IntPtr thisPtr, ref Vector3 up);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetClimbingMode(IntPtr thisPtr, CharacterClimbingMode mode);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetNonWalkableMode(IntPtr thisPtr, CharacterNonWalkableMode mode);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMinMoveDistance(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetContactOffset(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetStepOffset(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetSlopeLimit(IntPtr thisPtr, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetLayer(IntPtr thisPtr, ulong layer);
+    }
+}

+ 9 - 4
MBansheeEngine/Physics/Rigidbody.cs

@@ -354,7 +354,7 @@ namespace BansheeEngine
         /// Moves the rigidbody to a specific position. This method will ensure physically correct movement, i.e. the body
         /// will collide with other objects along the way.
         /// </summary>
-        /// <param name="position">New position for the body.</param>
+        /// <param name="position">New position for the body, in world space.</param>
         public void Move(Vector3 position)
         {
             if (native != null)
@@ -365,7 +365,7 @@ namespace BansheeEngine
         /// Rotates the rigidbody. This method will ensure physically correct rotation, i.e. the body will collide with
         /// other objects along the way.
         /// </summary>
-        /// <param name="rotation">New orientation of the body.</param>
+        /// <param name="rotation">New orientation of the body, in world space.</param>
         public void Rotate(Quaternion rotation)
         {
             if (native != null)
@@ -399,7 +399,7 @@ namespace BansheeEngine
         /// momentum.
         /// </summary>
         /// <param name="force">Force to apply.</param>
-        /// <param name="position">World position to apply the force at.</param>
+        /// <param name="position">World space point to apply the force at.</param>
         /// <param name="mode">Determines what type of force was applied.</param>
         public void AddForceAtPoint(Vector3 force, Vector3 position, PointForceMode mode = PointForceMode.Force)
         {
@@ -423,7 +423,7 @@ namespace BansheeEngine
         /// <summary>
         /// Triggered when one of the child colliders begins touching another object.
         /// </summary>
-        /// <param name="data">Data about a collision.</param>
+        /// <param name="data">Data about the collision.</param>
         internal void DoOnCollisionBegin(CollisionData data)
         {
             if (OnCollisionBegin != null)
@@ -507,6 +507,11 @@ namespace BansheeEngine
             }
         }
 
+        private void OnInitialize()
+        {
+            NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
+        }
+
         private void OnReset()
         {
             RestoreNative();

+ 63 - 0
SBansheeEngine/Include/BsScriptCharacterController.h

@@ -0,0 +1,63 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsCharacterController.h"
+#include "BsScriptControllerCollision.h"
+
+namespace BansheeEngine
+{
+	/** Interop class between C++ & CLR for CharacterController. */
+	class BS_SCR_BE_EXPORT ScriptCharacterController : public ScriptObject<ScriptCharacterController>
+	{
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "NativeCharacterController")
+	public:
+		/** Returns the native CharacterController object. */
+		CharacterController* getCharController() const { return mCharacterController.get(); }
+
+	private:
+		friend class ScriptColliderBase;
+
+		ScriptCharacterController(MonoObject* instance, const SPtr<CharacterController>& controller);
+
+		/** Triggered when the controller hits a collider. */
+		static void onColliderHit(MonoObject* instance, const ControllerColliderCollision& collisionData);
+
+		/** Triggered when the controller hits another controller. */
+		static void onControllerHit(MonoObject* instance, const ControllerControllerCollision& collisionData);
+
+		SPtr<CharacterController> mCharacterController;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static void internal_CreateInstance(MonoObject* instance, CHAR_CONTROLLER_DESC* initData);
+		static void internal_Destroy(ScriptCharacterController* thisPtr);
+
+		static CharacterCollisionFlags internal_Move(ScriptCharacterController* thisPtr, Vector3* displacement);
+
+		static void internal_GetPosition(ScriptCharacterController* thisPtr, Vector3* position);
+		static void internal_SetPosition(ScriptCharacterController* thisPtr, Vector3* position);
+
+		static void internal_GetFootPosition(ScriptCharacterController* thisPtr, Vector3* position);
+		static void internal_SetFootPosition(ScriptCharacterController* thisPtr, Vector3* position);
+
+		static void internal_SetRadius(ScriptCharacterController* thisPtr, float radius);
+		static void internal_SetHeight(ScriptCharacterController* thisPtr, float height);
+		static void internal_SetUp(ScriptCharacterController* thisPtr, Vector3* up);
+		static void internal_SetClimbingMode(ScriptCharacterController* thisPtr, CharacterClimbingMode mode);
+		static void internal_SetNonWalkableMode(ScriptCharacterController* thisPtr, CharacterNonWalkableMode mode);
+		static void internal_SetMinMoveDistance(ScriptCharacterController* thisPtr, float value);
+		static void internal_SetContactOffset(ScriptCharacterController* thisPtr, float value);
+		static void internal_SetStepOffset(ScriptCharacterController* thisPtr, float value);
+		static void internal_SetSlopeLimit(ScriptCharacterController* thisPtr, float value);
+		static void internal_SetLayer(ScriptCharacterController* thisPtr, UINT64 layer);
+
+		typedef void(__stdcall *OnHitThunkDef) (MonoObject*, ScriptControllerCollision*, MonoException**);
+
+		static OnHitThunkDef onColliderHitThunk;
+		static OnHitThunkDef onControllerHitThunk;
+	};
+}

+ 0 - 1
SBansheeEngine/Include/BsScriptControllerCollision.h

@@ -18,7 +18,6 @@ namespace BansheeEngine
 		MonoObject* collider;
 		int triangleIndex;
 		MonoObject* controller;
-		bool isControllerCollision;
 	};
 
 	/** Helper class for dealing with ControllerCollision structure. */

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -278,6 +278,7 @@
     <ClInclude Include="Include\BsScriptBuiltin.h" />
     <ClInclude Include="Include\BsScriptCamera.h" />
     <ClInclude Include="Include\BsScriptCapsuleCollider.h" />
+    <ClInclude Include="Include\BsScriptCharacterController.h" />
     <ClInclude Include="Include\BsScriptCollider.h" />
     <ClInclude Include="Include\BsScriptCollisionData.h" />
     <ClInclude Include="Include\BsScriptColor.h" />
@@ -391,6 +392,7 @@
     <ClCompile Include="Source\BsScriptBuiltin.cpp" />
     <ClCompile Include="Source\BsScriptCamera.cpp" />
     <ClCompile Include="Source\BsScriptCapsuleCollider.cpp" />
+    <ClCompile Include="Source\BsScriptCharacterController.cpp" />
     <ClCompile Include="Source\BsScriptCollider.cpp" />
     <ClCompile Include="Source\BsScriptCollisionData.cpp" />
     <ClCompile Include="Source\BsScriptColor.cpp" />

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -407,6 +407,9 @@
     <ClInclude Include="Include\BsScriptControllerCollision.h">
       <Filter>Header Files\Physics</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptCharacterController.h">
+      <Filter>Header Files\Physics</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -751,5 +754,8 @@
     <ClCompile Include="Source\BsScriptControllerCollision.cpp">
       <Filter>Source Files\Physics</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptCharacterController.cpp">
+      <Filter>Source Files\Physics</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 144 - 0
SBansheeEngine/Source/BsScriptCharacterController.cpp

@@ -0,0 +1,144 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptCharacterController.h"
+#include "BsMonoUtil.h"
+#include "BsMonoClass.h"
+#include "BsMonoMethod.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptCharacterController::OnHitThunkDef ScriptCharacterController::onColliderHitThunk = nullptr;
+	ScriptCharacterController::OnHitThunkDef ScriptCharacterController::onControllerHitThunk = nullptr;
+
+	ScriptCharacterController::ScriptCharacterController(MonoObject* instance, const SPtr<CharacterController>& charController)
+		:ScriptObject(instance), mCharacterController(charController)
+	{
+		charController->onColliderHit.connect(std::bind(&ScriptCharacterController::onColliderHit, instance, _1));
+		charController->onControllerHit.connect(std::bind(&ScriptCharacterController::onControllerHit, instance, _1));
+	}
+
+	void ScriptCharacterController::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptCharacterController::internal_CreateInstance);
+		metaData.scriptClass->addInternalCall("Internal_Destroy", &ScriptCharacterController::internal_Destroy);
+		metaData.scriptClass->addInternalCall("Internal_Move", &ScriptCharacterController::internal_Move);
+		metaData.scriptClass->addInternalCall("Internal_GetPosition", &ScriptCharacterController::internal_GetPosition);
+		metaData.scriptClass->addInternalCall("Internal_SetPosition", &ScriptCharacterController::internal_SetPosition);
+		metaData.scriptClass->addInternalCall("Internal_GetFootPosition", &ScriptCharacterController::internal_GetFootPosition);
+		metaData.scriptClass->addInternalCall("Internal_SetFootPosition", &ScriptCharacterController::internal_SetFootPosition);
+		metaData.scriptClass->addInternalCall("Internal_SetRadius", &ScriptCharacterController::internal_SetRadius);
+		metaData.scriptClass->addInternalCall("Internal_SetHeight", &ScriptCharacterController::internal_SetHeight);
+		metaData.scriptClass->addInternalCall("Internal_SetClimbingMode", &ScriptCharacterController::internal_SetClimbingMode);
+		metaData.scriptClass->addInternalCall("Internal_SetNonWalkableMode", &ScriptCharacterController::internal_SetNonWalkableMode);
+		metaData.scriptClass->addInternalCall("Internal_SetMinMoveDistance", &ScriptCharacterController::internal_SetMinMoveDistance);
+		metaData.scriptClass->addInternalCall("Internal_SetContactOffset", &ScriptCharacterController::internal_SetContactOffset);
+		metaData.scriptClass->addInternalCall("Internal_SetSlopeLimit", &ScriptCharacterController::internal_SetSlopeLimit);
+		metaData.scriptClass->addInternalCall("Internal_SetLayer", &ScriptCharacterController::internal_SetLayer);
+
+		onColliderHitThunk = (OnHitThunkDef)metaData.scriptClass->getMethod("Internal_DoOnColliderHit", 1)->getThunk();
+		onControllerHitThunk = (OnHitThunkDef)metaData.scriptClass->getMethod("Internal_DoOnControllerHit", 1)->getThunk();
+	}
+
+	void ScriptCharacterController::onColliderHit(MonoObject* instance, const ControllerColliderCollision& collisionData)
+	{
+		ScriptControllerCollision scriptCollisionData = ScriptControllerCollisionHelper::create(collisionData);
+		MonoUtil::invokeThunk(onColliderHit, instance, &scriptCollisionData);
+	}
+
+	void ScriptCharacterController::onControllerHit(MonoObject* instance, const ControllerControllerCollision& collisionData)
+	{
+		ScriptControllerCollision scriptCollisionData = ScriptControllerCollisionHelper::create(collisionData);
+		MonoUtil::invokeThunk(onControllerHit, instance, &scriptCollisionData);
+	}
+
+	void ScriptCharacterController::internal_CreateInstance(MonoObject* instance, CHAR_CONTROLLER_DESC* initData)
+	{
+		SPtr<CharacterController> charController = CharacterController::create(*initData);
+		charController->_setOwner(PhysicsOwnerType::Script, instance);
+
+		ScriptCharacterController* scriptCharacterController = 
+			new (bs_alloc<ScriptCharacterController>()) ScriptCharacterController(instance, charController);
+	}
+
+	void ScriptCharacterController::internal_Destroy(ScriptCharacterController* thisPtr)
+	{
+		thisPtr->mCharacterController = nullptr;
+	}
+
+	CharacterCollisionFlags ScriptCharacterController::internal_Move(ScriptCharacterController* thisPtr, Vector3* displacement)
+	{
+		return thisPtr->mCharacterController->move(*displacement);
+	}
+
+	void ScriptCharacterController::internal_GetPosition(ScriptCharacterController* thisPtr, Vector3* position)
+	{
+		*position = thisPtr->mCharacterController->getPosition();
+	}
+
+	void ScriptCharacterController::internal_SetPosition(ScriptCharacterController* thisPtr, Vector3* position)
+	{
+		thisPtr->mCharacterController->setPosition(*position);
+	}
+
+	void ScriptCharacterController::internal_GetFootPosition(ScriptCharacterController* thisPtr, Vector3* position)
+	{
+		*position = thisPtr->mCharacterController->getFootPosition();
+	}
+
+	void ScriptCharacterController::internal_SetFootPosition(ScriptCharacterController* thisPtr, Vector3* position)
+	{
+		thisPtr->mCharacterController->setFootPosition(*position);
+	}
+
+	void ScriptCharacterController::internal_SetRadius(ScriptCharacterController* thisPtr, float radius)
+	{
+		thisPtr->mCharacterController->setRadius(radius);
+	}
+
+	void ScriptCharacterController::internal_SetHeight(ScriptCharacterController* thisPtr, float height)
+	{
+		thisPtr->mCharacterController->setHeight(height);
+	}
+
+	void ScriptCharacterController::internal_SetUp(ScriptCharacterController* thisPtr, Vector3* up)
+	{
+		thisPtr->mCharacterController->setUp(*up);
+	}
+
+	void ScriptCharacterController::internal_SetClimbingMode(ScriptCharacterController* thisPtr, CharacterClimbingMode mode)
+	{
+		thisPtr->mCharacterController->setClimbingMode(mode);
+	}
+
+	void ScriptCharacterController::internal_SetNonWalkableMode(ScriptCharacterController* thisPtr, CharacterNonWalkableMode mode)
+	{
+		thisPtr->mCharacterController->setNonWalkableMode(mode);
+	}
+
+	void ScriptCharacterController::internal_SetMinMoveDistance(ScriptCharacterController* thisPtr, float value)
+	{
+		thisPtr->mCharacterController->setMinMoveDistance(value);
+	}
+
+	void ScriptCharacterController::internal_SetContactOffset(ScriptCharacterController* thisPtr, float value)
+	{
+		thisPtr->mCharacterController->setContactOffset(value);
+	}
+
+	void ScriptCharacterController::internal_SetStepOffset(ScriptCharacterController* thisPtr, float value)
+	{
+		thisPtr->mCharacterController->setStepOffset(value);
+	}
+
+	void ScriptCharacterController::internal_SetSlopeLimit(ScriptCharacterController* thisPtr, float value)
+	{
+		thisPtr->mCharacterController->setSlopeLimit(Radian(value));
+	}
+
+	void ScriptCharacterController::internal_SetLayer(ScriptCharacterController* thisPtr, UINT64 layer)
+	{
+		thisPtr->mCharacterController->setLayer(layer);
+	}
+}

+ 0 - 2
SBansheeEngine/Source/BsScriptControllerCollision.cpp

@@ -21,7 +21,6 @@ namespace BansheeEngine
 		if (data.colliderRaw != nullptr)
 			output.collider = (MonoObject*)data.colliderRaw->_getOwner(PhysicsOwnerType::Script);
 
-		output.isControllerCollision = false;
 		output.motionAmount = data.motionAmount;
 		output.motionDir = data.motionDir;
 		output.normal = data.normal;
@@ -38,7 +37,6 @@ namespace BansheeEngine
 		if (data.controllerRaw != nullptr)
 			output.controller = (MonoObject*)data.controllerRaw->_getOwner(PhysicsOwnerType::Script);
 
-		output.isControllerCollision = true;
 		output.motionAmount = data.motionAmount;
 		output.motionDir = data.motionDir;
 		output.normal = data.normal;