I'm creating a platform system using a raycast controller that uses an interface to perform different tasks based on the type of platform with which my player is currently colliding. Some of the platform types include ice, passable blocks and muddy ground.
I want to know how to better optimize my code, as I currently call Unity's somewhat expensive "GetComponent()" function every frame, even if I never change between blocks. What I'd like to do is only call GetComponent() when I change from one type of platform to a different type of platform (i.e. muddy ground --> ice), but don't know how to do this using an interface.
I thought I would be able to compare types using enums, but you're not allowed to declare types in an interface.
if (hit)
{
//I'd only like to run this block of code if the type of platform changes
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
State.IsCollidingWithPassablePlatform = platform.IsPassable;
State.IsJumpBoosted = platform.IsJumpForce;
State.IsBoosted = platform.IsForce;
xForce = platform.XForce;
yForce = platform.YForce;
zForce = platform.ZForce;
defaultParameters.accelerationTimeGrounded = platform.AccelerationTimeGrounded;
defaultParameters.accelerationTimeAirborne = platform.AccelerationTimeAirborne;
Interface example:
interface IPlatform {
float AccelerationTimeGrounded { get; }
float AccelerationTimeAirborne { get; }
float XForce { get; }
float YForce { get; }
float ZForce { get; }
bool IsPassable { get; }
bool IsForce { get; }
bool IsJumpForce { get; }
Ice platform:
public class PlatformIce : MonoBehaviour, IPlatform {
public float AccelerationTimeGrounded { get { return accelerationTimeGrounded; } }
public float AccelerationTimeAirborne { get { return accelerationTimeAirborne; } }
public float XForce { get { return xForce; } }
public float YForce { get { return yForce; } }
public float ZForce { get { return zForce; } }
public virtual bool IsPassable { get { return false; } }
public bool IsForce { get { return false; } }
public bool IsJumpForce { get { return false; } }
[SerializeField]
private float accelerationTimeGrounded = 1.0f;
[SerializeField]
private float accelerationTimeAirborne = 3.0f;
private float xForce = 0;
private float yForce = 0;
private float zForce = 0;
}
Remember your last GameObject and check if this one has changed
private lastGameObj;
[...]
if(lastGameObj!= hit.collider.gameObject) {
var platform = hit.collider.gameObject.GetComponent<IPlatform>();
// [...] your magic here
lastGameObj= hit.collider.gameObject;
}
You will get an additional condition, but you won't run your code 60 times/sec inclusive that GetComponent();.
You CAN use enums inside an Interface, you just have to declare the enum type outside the Interface.
I.E.:
public enum PlatformType {Normal, Ice, Fire, etc}; //Use public only if needed, of course
interface IPlatform {
PlatformType platformType { get; }
//Your other stuff here
}
This will break encapsulation, clearly, but if you really want to use an enum in an interface, there's no way around it.
Related
I have a bunch of different kind of NPCs in my game and of course they logically similar, they have health, they have vision, they can navigate using agent and stuff.
But each NPC type has it's own custom behavior with states, actions, decisions and hooks. And those scripts require various specific data like coroutines running, target altitude or current leaping direction.
And I have to store it or have it on NPC mono behavior, so it is accessible inside state's scripts (they are scriptable objects called from NPC mono behavior)
Right now what I do is specifying array for each data type and count of it that I assign on NPC prefab. And it feels wrong...
public class Npc : MonoBehaviour
{
public static Dictionary<int, Npc> npcs = new Dictionary<int, Npc>();
public int npcId;
public NpcType type;
public Transform shootOrigin;
public Transform head;
public float maxHealth = 50f;
public float visionRange = 15;
public float visionAngle = 60;
public float headAngle = 120;
public float movementSpeed = 4.5f;
public int indexedActionsCount = 0;
[HideInInspector] public float[] lastActTimeIndexed;
[HideInInspector] public bool[] wasActionCompletedIndexed;
public int indexedVector3DataCount = 0;
[HideInInspector] public Vector3[] vector3DataIndexed;
public int indexedFloatDataCount = 0;
[HideInInspector] public float[] floatDataIndexed;
public int indexedBoolDataCount = 0;
[HideInInspector] public bool[] boolDataIndexed;
public int indexedCoroutineDataCount = 0;
[HideInInspector] public IEnumerator[] coroutineDataIndexed;
public NpcState currentState;
public NpcState remainState;
public float Health { get; private set; }
[HideInInspector] public NavMeshAgent agent;
public static int decisionUpdatesPerSecond = 2; // Check for decisions in 2FPS
public static int actionUpdatesPerSecond = 5; // Act in 5FPS
public static int reportUpdatesPerSecond = 15; // Report in 15FPS
private static int nextNpcId = 10000;
public void Awake()
{
agent = GetComponent<NavMeshAgent>();
}
public void Start()
{
npcId = nextNpcId;
nextNpcId++;
npcs.Add(npcId, this);
Health = maxHealth;
agent.speed = movementSpeed;
lastActTimeIndexed = new float[indexedActionsCount];
wasActionCompletedIndexed = new bool[indexedActionsCount];
floatDataIndexed = new float[indexedFloatDataCount];
boolDataIndexed = new bool[indexedBoolDataCount];
vector3DataIndexed = new Vector3[indexedVector3DataCount];
coroutineDataIndexed = new IEnumerator[indexedCoroutineDataCount];
ServerSend.SpawnNpc(npcId, type, transform.position);
InvokeRepeating("GetTarget", 1.0f, 1.0f);
InvokeRepeating("UpdateDecisions", 0.0f, 1.0f / decisionUpdatesPerSecond);
InvokeRepeating("UpdateActions", 0.0f, 1.0f / actionUpdatesPerSecond);
InvokeRepeating("SendUpdates", 0.0f, 1.0f / reportUpdatesPerSecond);
OnEnterState();
}
public void TakeDamage(float _damage)
{
}
public bool GoTo(Vector3 location)
{
}
public void TransitionToState(NpcState nextState)
{
OnExitState();
currentState = nextState;
OnEnterState();
}
public void StartCoroutineOnNpc(IEnumerator routine)
{
StartCoroutine(routine);
}
public void StopCoroutineOnNpc(IEnumerator routine)
{
StopCoroutine(routine);
}
private void OnEnterState()
{
var hooks = currentState.onEnterHooks;
for (int i = 0; i < hooks.Length; i++)
{
hooks[i].Apply(this);
}
stateTimeOnEnter = Time.time;
wasActionCompleted = false;
}
private void OnExitState()
{
var hooks = currentState.onExitHooks;
for (int i = 0; i < hooks.Length; i++)
{
hooks[i].Apply(this);
}
}
private void UpdateDecisions()
{
currentState.UpdateDecisions(this);
}
private void UpdateActions()
{
currentState.UpdateState(this);
}
private void SendUpdates()
{
ServerSend.NpcState(this);
}
}
In JavaScript world I would just have 1 array or object and put any data this particular NPC needs to it. But in C# I need a strongly typed place to put data for each data type my scripts could require.
Example of data usage in script:
I don't think having so many arrays and counters on MonoBehavior is a good idea, especially that there may be a lot of NPCs on scene. Any advice on building better storage while maintaining script flexibility?
Clarification:
All the behavior logic is controlled by flexible ScriptableObject states. The problem is these objects cannot store any runtime data, but they have access to my Npc MonoBehavior (component) instance.
Initial code for this approach came from Unity tutorial
Let me explain the structure I ended up using for the case I described:
If particular NPC requires some specific data for its behavior I will add another component (in this example leaper NPC needs to store data for leaping behavior)
This data is defined in interface (it's important, because 1 NPC may implement multiple interfaces [several reused behaviors])
public interface ILeaperData
{
public Vector3 leapTarget { get; set; }
public Vector3 initialPosition { get; set; }
public bool startedLeap { get; set; }
public float lastLeapTime { get; set; }
}
And then this NPC type will have component that implements this interface (and 1 more in this example)
public class LeaperData : NpcData, ILeaperData, ICompletedActionData
{
public Vector3 leapTarget { get; set; }
public Vector3 initialPosition { get; set; }
public bool startedLeap { get; set; }
public float lastLeapTime { get; set; }
public bool wasActionCompleted { get; set; }
}
That way I can reuse data interfaces when the same behavior is used on other NPC types.
Example of how it is used in ScriptableObject logic:
[CreateAssetMenu(menuName = "AI/Decisions/CanLeap")]
public class CanLeapDecision : NpcDecision
{
public int nextAngle = 45;
public float radius = 4;
public override bool Decide(Npc npc)
{
if (npc.target)
{
var dir = (npc.transform.position - npc.target.position).normalized;
var dir2 = new Vector2(dir.x, dir.z).normalized * radius;
var dir3 = new Vector3(dir2.x, dir.y, dir2.y);
if (NavMesh.SamplePosition(RotateAroundPoint(npc.target.position + dir3, npc.target.position, Quaternion.Euler(0, nextAngle * ((Random.value > 0.5f) ? 1 : -1), 0)), out var hit, 3.5f, 1))
{
var path = new NavMeshPath();
npc.agent.CalculatePath(hit.position, path);
if (path.corners.Length == 2 && path.status == NavMeshPathStatus.PathComplete)
{
((ILeaperData)npc.npcData).leapTarget = hit.position;
((ILeaperData)npc.npcData).initialPosition = npc.transform.position;
((ILeaperData)npc.npcData).startedLeap = false;
return true;
}
}
}
return false;
}
private Vector3 RotateAroundPoint(Vector3 point, Vector3 pivot, Quaternion angle)
{
var finalPos = point - pivot;
//Center the point around the origin
finalPos = angle * finalPos;
//Rotate the point.
finalPos += pivot;
//Move the point back to its original offset.
return finalPos;
}
}
You can see the cast to (ILeaperData) where I need the data stored on this NPC instance.
So yeah, i feel really dumb to ask this question, but i'm currently in the process of writing a simple pathfinder script. I want to use dictionaries like
Dictionary<Floor, FloorInfo>
where floor is the floor tile i am referencing and FloorInfo is custom class as follows:
public class FloorInfo
{
Floor lastFloor;
float floorValue;
public FloorInfo(Floor lastF, float val)
{
lastFloor = lastF;
floorValue = val;
} }
But after i create something like
FloorInfo info = new FloorInfo(current, F);
I cannot get the values, like info.val or info.lastF
Could you explain to me what am I doing wrong? I feel really awkward that i got stuck on something like that or past 45 minutes.
EDIT: Okay, thank you everyone who already answered. Seems like most obvious things can be quite problematic as well. Thanks again and have a nice day!
Make them public if you want to access them from outside the class.
You must mark the fields lastFloot and floorValue as public, or better yet provide a public property for accessing those private fields, like this:
public class FloorInfo
{
private Floor m_lastFloor;
private float m_floorValue;
public Floor LastFloor {
get { return m_lastFloor; }
}
public float FloorValue {
get { return m_floorValue }
}
public FloorInfo(Floor lastF, float val)
{
m_lastFloor = lastF;
m_floorValue = val;
}
}
Then you can access the values like this:
FloorInfo info = new FloorInfo(current, F);
float value = info.FloorValue;
The lastF and val are parameters to your constructor. These are gone as soon as the constructor completes.
You have copied these values to lastFloor and floorValue but currently they are private. You should make these public. If you dont specify a modifier then by default it is private and is not visible outside of the class that they are defined.
public class FloorInfo
{
public Floor lastFloor;
public float floorValue;
public FloorInfo(Floor lastF, float val)
{
lastFloor = lastF;
floorValue = val;
}
}
so you can then reference info.floorValue and info.LastFloor
If you want good design then you should make these into properties and possibly make the set private so they it cannot be changed outside of the FloorInfo class. Also make the properties start with capital letters.
public class FloorInfo
{
public Floor LastFloor { get; private set; }
public float FloorValue { get; private set; }
public FloorInfo(Floor lastF, float val)
{
lastFloor = lastF;
floorValue = val;
}
}
That is because C# class' field's access modifier (by default) is private. What you do above is trying to access private field outside of the scope of the class (which is not allowed).
To access the fields, make its access modifiers public, then you can access them outside of the class scope:
public class FloorInfo
{
public Floor lastFloor; //note the public keyword
public float floorValue;
public FloorInfo(Floor lastF, float val)
{
lastFloor = lastF;
floorValue = val;
}
}
And simply access the fields like:
FloorInfo info = new FloorInfo(current, F);
info.lastFloor = new Floor();
info.floorValue = 45.0;
Note that you do not access the lastF and val from above since they are simply your constructor's parameters. You access the fields of your class, not its constructor's parameters.
That being said, it is more common to access them as property rather than field.
public Floor lastFloor { get; set; }
public float floorValue { get; set; }
This is because with property, you could set something else in your getter and setter (such as checking if the inputs for your property is valid), which is, most of the time, a safer design:
const float floorValueLimit = 20.0;
private float pFloorValue;
public float floorValue {
get { return pFloorValue; }
set {
if (value <= floorValueLimit){ //check limit
pFloorValue = value;
} //else, don't update
}
}
But you cannot do this using field.
Also, as an additional side note, public field would normally have capital letter as its first character in C# typical naming convention:
public class FloorInfo
{
public Floor LastFloor; //note the public keyword
public float FloorValue;
public FloorInfo(Floor lastF, float val)
{
lastFloor = lastF;
floorValue = val;
}
}
I am having a lot of trouble with the syntax and the rules for using Generics. I am trying to make a structure, where different classes, can use the WaitAction class to disable input while a couroutine is running, an re-enable it once the coroutine is finished.
This example is a simplified version, and in reality I will not be using a count float to define the length of the coroutine, but the length will based on animations and translation.
Is what I am trying to do at all possible?
"Somehow use "T _ready" to change the "bool ready" in "Main Class" back to "true""
public class Main : Monobehaviour {
WaitAction _waitAction = new WaitAction();
public bool ready;
float delay = 5f;
void Update()
{
if(Input.GetMouseButton(0) && ready)
{
ready = false;
StartCoroutine(_waitAction.SomeCoroutine((delay, this));
}
}
public class WaitAction {
public IEnumerator SomeCoroutine<T>(float count, T _ready)
{
float time = Time.time;
while(Time.time < time + count)
{
yield return null;
}
// Somehow use "T _ready" to change the "bool ready" in "Main Class" back to "true"
}
}
The solution is to constrain the generic type, such that the generic method knows how to set the ready flag. This is easily done using an interface:
public interface IReady
{
bool ready { get; set; }
}
public class Main : Monobehaviour, IReady {
...
public bool bool ready { get; set; }
...
}
public class WaitAction {
public IEnumerator SomeCoroutine<T>(float count, T _ready) where T : IReady
{
...
_ready.Ready = true;
}
}
If I have a Struct or a Class, lets say I'm using a Vector2 (which has two members, float X and float Y), how am I supposed to properly get/set its methods in a class?
This is the code I know of right now:
public class MyClass
{
private Vector2 vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
}
But what if I wanted to edit the members (or certain members) of the Vector2 with the set method? Something like this is what I'm asking for:
set.X
{
vector.X = value.X;
}
set.Y
{
vector.Y = value.Y;
}
And it would be called as simply as Vector.X = 5. I thought up an alternative, using something like public float VectorX { set { vector.X = value; } } but I would prefer a more logical and object-orientated way. Is there?
It makes a BIG difference whether Vector2 is a class or a struct.
Since Vector2 is a class you can simply do
obj.Vector.X = 5;
having
public class MyClass
{
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
}
If, however Vector2 was a struct then you cannot modify the return value from the get. If you try you will get a compile error:
Cannot modify the return value of ... because it is not a variable.
You can work around this using the method you suggested with
public float VectorX
{
get { return _vector.X; }
set { _vector.X = value; }
}
public float VectorY
{
get { return _vector.Y; }
set { _vector.Y = value; }
}
or you could provide a wrapper class around Vector2 like:
class Vector2Wrapper
{
public Vector2 Vector;
}
and then store a Vector2Wrapper in MyClass like
public class MyClass
{
private Vector2Wrapper _vector2Wrapper;
public Vector2Wrapper VectorWrapper
{
get { return _vector2Wrapper; }
set { _vector2Wrapper= value; }
}
}
and you can then modify it like
obj.VectorWrapper.Vector.X = 5;
you can't specify a submethod to handle that partial set, because that set is handled by the Vector class and us thus out of your scope. the moment someone calls Myobject.Vector they are calling your get funcion, but when it moves on to .X they are calling the Vector.get_X function.
This may be easier to see by looking at the compiled code using the ILDasm tool, which reveals the actual method calls your properties make sugar out of.
Now, what you CAN do is to wrap certain properties, as mentioned. The result would like like so.
public class MyClass
{
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
public float VectorX
{
get { return _vector.X; }
set { _vector.X = value; }
}
public float VectorY
{
get { return _vector.Y; }
set { _vector.Y = value; }
}
}
Another option might be to use the pattern of INotifyPropertyChanged in your Vector class, raising an event every change which your MyClass can then listen to and react to, thus applying logic when child elements are updated.
There is also the option of using an indexer property
public class MyClass
{
public enum Axis { X, Y }
private Vector2 _vector; //Has to be private
public Vector2 Vector
{
get { return vector; }
set { vector = value; }
}
public float this[Axis axis]
{
get { return axis == Axis.X ? vector.x : vector.y; }
set
{
if(axis == Axis.Y)
{
// Special logic here
vector.Y = value;
}
if(axis == Axis.X)
{
// Special logic here
vector.X = value;
}
}
}
}
Because Vector2 is a struct, you are getting a COPY. You need to use the setter to set a new Vector2. There are 2 ways:
Vector2 v = myClass.Vector; //get a copy of the vector
v.X = 5f; // change its value
myClass.Vector = v; // put it back
I don't really like the following, but it's one statement:
myClass.Vector = new Vector2(2f, myClass.Vector.Y)
Inside MyClass, you can create a property that sets only the X value:
public float X {
get { return Vector.X; }
set {
Vector2 v = Vector;
v.X = value;
Vector = v;
}
}
(Vector can be an automatic property)
I have a C# game, I wish to allow our scripter to write small code files such as what is below so we can patch and mod some of our systems.
namespace Game.Creatures
{
public class Zombie : Creature
{
public override float MovementRange { get { return 12; } }
public override float AttackDamage { get { return 5; } }
public override float TurnSpeed { get { return 2; } }
public override float WalkSpeed { get { return 1.7f; } }
public override float RunSpeed { get { return 2.5f; } }
public override float TimeBetweenAttacksSeconds { get { return 0; } }
public override bool CanAttack { get { return false; } }
public Zombie(Vector3 in_Position, Vector3 in_Scale)
{
Health = MaxHealth = 40;
CurrentAi = new Ai_Simple(this) {RecalcPathIn = -1};
}
public override void Update(GameTime in_GameTime) { base.Update(in_GameTime); }
public override void Render(GameTime in_GameTime) { base.Render(in_GameTime); }
}
}
As you can see the file uses types and methods from the game, is this possible or does any runtime created code need to be confined to itself?
Bonus if it also works on the 360.
Edit: Paint.net has a code lab plugin that does exactly this, so I know it must be possible.
Is it possible to dynamically compile and execute C# code fragments? deals with the same thing. Being able to reference your classes just means that one of the assemblies you want to reference should be the one with Creature in it. E.g.
var parameters = new CompilerParameters(
new[] { "mscorlib.dll", "System.Core.dll", "MyAssembly.dll" }, "foo.exe", true);