I really need help on this so please be patient.
Alright, so I've recently started to work on my first game, very basic.
I decided to create a GameObject class. That will contain the basics of my other classes (e.g : Player, Enemies).
So, this is the currently code of the GameObject class:
abstract class GameObject
{
GraphicsDevice gr;
Vector2 position;
Texture2D texture;
public GameObject(Vector2 Position, Texture2D Texture)
{
this.position = Vector2.Zero;
this.texture = Texture;
}
public Vector2 Position { set; get; }
public Texture2D Texture { set; get; }
public float X
{
set { position.X = value; }
get { return position.X; }
}
public float Y
{
set
{
position.Y = value;
}
get
{
return position.Y;
}
}
public int GraphicsWidth { set; get; }
public int GraphicsHeight { set; get; }
}
OK, so I wanted to set the GraphicsWidth and GraphicsHeight variables from the Main Class (Game1.cs) so in the Initialize method I've done this:
GraphicsHeight = graphics.PreferredBackBufferHeight;
GraphicsWidth = graphics.PreferredBackBufferWidth;
But it says that GraphicsHeight doesn't exist in the current context.
I know I'm missing something but I don't know what.
BTW, Is there anything wrong or anything that I can do better with my GameObject class?
Thanks a lot.
You must have another, concrete class inherit your abstract GameObject. For instance:
public class Player : GameObject
{
/* methods properties specific to player */
}
After instantiation, you will then be able to set those properties:
Player.GraphicsHeight = graphics.PreferredBackBufferHeight;
Player.GraphicsWidth = graphics.PreferredBackBufferWidth;
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.
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.
I need a little help for my inheritance of my player in my little 2D Game.
In fact, I don't know how I can create a new player, and add it to a list.
This is my project structure :
As you can see, in my scripts, I have a GameBase script, and PlayerBase script with PlayerLife, PlayerController and PlayerAimManager as child of PlayerBase.
In my prefabs, I have my camera with GameBase script, and PlayerPrefab, with player scripts.
My PlayerBase is a script witch define a player, like his life, speed, color etc... :
public class PlayerBase : MonoBehaviour
{
public int Id { get; set; }
public string Name { get; set; }
protected float Life { get; set; }
public Color Color { get; set; }
protected WeaponBase PrimaryWeapon { get; set; }
protected WeaponBase SecondaryWeapon { get; set; }
protected WeaponBase SelectedWeapon { get; set; }
protected ScoreBase Score { get; set; }
protected bool IsDead { get; set; }
protected float Speed { get; set; }
protected float JumpForce { get; set; }
protected virtual void Start()
{
IsDead = false;
//Reset rotation
transform.rotation = Quaternion.identity;
Life = 100;
Speed = 20;
JumpForce = 4000;
PrimaryWeapon = gameObject.AddComponent<SMG>();
SecondaryWeapon = gameObject.AddComponent<Shotgun>();
SelectedWeapon = PrimaryWeapon;
}
}
(And my child works like this :)
public class PlayerLife : PlayerBase
{
// Use this for initialization
protected override void Start()
{
base.Start();
var color = base.Color();
}
}
Ok, now this is my problem :
How can I instantiate a new player and save it in list.
In my GameBase I do something like this:
public class GameBase : MonoBehaviour
{
public List<PlayerBase> Players { get; set; }
void Start()
{
var playerObj1 = Instantiate(Resources.Load("Prefabs/Players/PlayerPrefab")) as GameObject;
PlayerBase player1 = playerObj1.GetComponent<PlayerBase>();
player1.Color = Color.red;
player1.Name = "Naografix Red";
var playerObj2 = Instantiate(Resources.Load("Prefabs/Players/PlayerPrefab")) as GameObject;
PlayerBase player2 = playerObj2.GetComponent<PlayerBase>();
player2.Color = Color.blue;
player2.Name = "Foo Blue";
Players = new List<PlayerBase>{
player1,
player2
}
}
}
If I do this, my color in my child player scripts don't have my color because is not static. But if I put Color to static prop, I can't change my color in my GameBase.
Well, I don't know if my inheritance is good or not.
I just want to create a player dynamically with child script based on PlayerBase to get his own variable.
EDIT
In fact, I don't know how I can create dynamically a player with custom properties.
I want to do this :
PlayerBase player1 = new PlayerBase();
player1.Name = "Nao";
player.Color = Color.Red;
And store it in my PlayerList. But I can't do this, because, no prefab is created.
So I instantiate a new GameObject with my PlayerPrefab.
-Ok, cool, my player is spawned, I'm going to apply some properties. Huh? How can I get my PlayerBase script?
Then, I get my PlayerBase with GetComponent, but still not working because I got my child (I don't have PlayerBase on my PlayerPrefab).
And if I put PlayerBase on it, every child have a different instance of my PlayerBase.
To conclude : How can I create a new Player based on PlayerBase, share all properties to my childs and spawn a PlayerPrefab with their properties ?
ENDEDIT
EDIT With simple example
public class Game : MonoBehaviour
{
private List<PlayerBase> _players;
public GameObject Player;
void Start () {
_players = new List<PlayerBase>();
var obj = Instantiate(Player);
var playerBase = obj.GetComponent<PlayerBase>();
playerBase.Color = Color.blue;
}
void Update () {
}
}
public class PlayerBase : MonoBehaviour {
public Color Color { get; set; }
// Use this for initialization
protected virtual void Start () {
gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color);
}
// Update is called once per frame
protected virtual void Update () {
}
}
public class PlayerLife : PlayerBase {
// Use this for initialization
protected override void Start () {
gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color);
}
// Update is called once per frame
void Update () {
}
}
My playerLife color is null. This is my first problem.
ENDEDIT
Ask me if you want more details.
Thanks
When you define a public member (not a property) in a MonoBehaviour, the value set by the inspector is automatically serialized for that game object. So that when you instantiate from that game object it will be automatically restored into the instantiated game object.
This also can be done without public accessor and with [SerializeField] attribute.
Make sure that only your PlayerLife script inherits from PlayerBase. The rest of your scripts(PlayerController and PlayerAimManager) should inherit from MonoBehaviour.
As, for your color problem, it is not showing because when you do player2.Color = Color.blue;, you modifying a struct which is a copy not a reference. You have to manually assign that color to gameObject.GetComponent<Renderer>().material.SetColor.
Your new PlayerBase script:
public class PlayerBase : MonoBehaviour
{
public Color Color;
public Color matColor
{
get
{
Color = gameObject.GetComponent<Renderer>().material.GetColor("_Color");
return Color;
}
set
{
Color = value;
gameObject.GetComponent<Renderer>().material.SetColor("_Color", Color);
}
}
// Use this for initialization
protected virtual void Start()
{
matColor = Color;
}
// Update is called once per frame
protected virtual void Update()
{
}
}
PlayerLife:
public class PlayerLife : PlayerBase
{
public PlayerBase pBase;
void Awake()
{
pBase = this;
}
// Use this for initialization
protected override void Start()
{
matColor = Color;
}
// Update is called once per frame
protected override void Update()
{
}
}
To use it from your game scriot:
public class Game : MonoBehaviour
{
private List<PlayerLife> _players;
public GameObject Player;
void Start()
{
_players = new List<PlayerLife>();
GameObject obj = Instantiate(Player);
PlayerLife playerLife = obj.GetComponent<PlayerLife>();
playerLife.matColor = Color.blue;
_players.Add(playerLife);
GameObject obj2 = Instantiate(Player) as GameObject;
PlayerLife playerLife2 = obj2.GetComponent<PlayerLife>();
playerLife2.Color = Color.red;
_players.Add(playerLife2);
}
void Update()
{
}
}
Now, if you decide to access PlayerBase from your PlayerController and PlayerAimManager script,
PlayerBase playerBase = gameObject.GetComponent<PlayerBase>();
OR
PlayerBase playerBase = gameObject.GetComponent<PlayerLife>().pBase;
so I'm creating a save and load script, here's it's content:
using UnityEngine;
using System.Collections;
public class SaveLoad : MonoBehaviour {
public Player player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player> ();
public void Save() {
PlayerPrefs.SetFloat ("X", transform.position.x);
PlayerPrefs.SetFloat ("Y", transform.position.y);
PlayerPrefs.SetFloat ("Z", transform.position.z);
PlayerPrefs.SetFloat("Health", Player.Health.CurrentVal);
PlayerPrefs.SetFloat ("Exp", Player.Exp.CurrentVal);
PlayerPrefs.SetFloat("Oxygen", Player.Oxygen.CurrentVal);
PlayerPrefs.SetFloat("PlayerLevel", Player.PlayerLevel.CurrentVal);
Debug.Log ("Saved");
}
public void Load() {
float x = PlayerPrefs.GetFloat ("X");
float y = PlayerPrefs.GetFloat ("Y");
float z = PlayerPrefs.GetFloat ("Z");
Player.Health.CurrentVal = PlayerPrefs.GetFloat("Health");
Player.Exp.CurrentVal = PlayerPrefs.GetFloat("Exp");
Player.Oxygen.CurrentVal = PlayerPrefs.GetFloat("Oxygen");
Player.PlayerLevel.CurrentVal = PlayerPrefs.GetFloat("PlayerLevel");
transform.position = new Vector3 (x, y, z);
Debug.Log ("Loaded");
}
}
The first version, without the "public Player player" line, returned a NullReferenceException and said to reference through an instance of an object in Unity. Now when I try to do that(instead of ("Health", Player.Health.CurrentVal) i use("Health", player.Health.CurrentVal), without capital in "Player", refering to the instance I creaded in the same script), Visual tells me "Player.Health cannot be accessed with an instance of an object; qualify it with a type name instead". As you can see, I'm kinda stuck in a loop here. Did I mess up with trying to get player as an instance? It's also a little bit of a mess cause Player is both the gameobject's name and the script's.
Player script:
[SerializeField] public Stat health;
public static Stat Health { get; set; }
[SerializeField] private Stat exp;
public static Stat Exp { get; set; }
[SerializeField] private Stat oxygen;
public static Stat Oxygen { get; set; }
[SerializeField] private Stat playerLevel;
public static Stat PlayerLevel { get; set; }
[SerializeField] private Text playerLevelText;
And here's the Stat custom variable class:
[SerializeField] private BarPrototype bar;
[SerializeField] private float maxVal;
[SerializeField] private float currentVal;
public float CurrentVal
{
get
{
return currentVal;
}
set
{
this.currentVal = Mathf.Clamp (value, 0, MaxVal);
if( bar != null )
bar.Value = this.currentVal;
}
}
SerializeField is used just to access those values from inspector.
The problem is you can't initialize public members like this:
public Player player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player> ();
They should be initialized in Start, Awake or OnEnable method. If you change it to below it should work
public Player player;
void Start(){
player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player> ();
}
Another thing is why should you make it public then? If assigned in inspector then its assigned value would be ignored after Start(). I suggest you make it internal. or to be safe:
public Player player;
void Start(){
if(player == null)
player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player> ();
}
I know that this is a pretty obvious question but from what I've seen I can't yet get it.
I don't exactly understand how Getters and Setters work in C#, for instance, I have this code for a character in a game I'm supposed to make for college:
namespace Clase_25_3_2014
{
class Brick
{
private int speed { get; set; }
private Vector2 pos { get; set; }
private Texture2D skin { get; set; }
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed(speed);
this.Pos(position);
this.Skin(skin);
}
}
}
Now, as it stands, where I use the class I try to call it like this:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
brick.GetPos();
Now, obviously that looks weird for you guys, and that's because I haven't yet found out how am I supposed to use it correctly so that it works like a brick.getPos(); from java.
Sorry for the really obvious question, but I can't seem to find an answer for me.
You can't do GetPos because pos is private and you don't have a method called "GetPos". If you make pos public, you can just use Brick.pos to get and Brick.pos(position) to set.
Here's how you could write this class:
namespace Clase_25_3_2014
{
class Brick
{
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
public int Speed { get; set; }
public Vector2 Pos { get; set; }
public Texture2D Skin { get; set; }
}
}
Types of Class access:
// lots of explicity (is that a word? :)
public MyClass
{
// Field
// It is recommended to never expose these as public
private int _myField;
// Property (old school, non-auto private field)
public int MyProperty
{
public get
{
return this._myField;
}
public set
{
this._myField = value;
}
}
// Property (new school, auto private field)
// (auto field cannot be accessed in any way)
public int MyProperty2 { public get; private set; }
// Method (these are not needed to get/set values for fields/properties.
public int GetMyMethod()
{
return this._myField;
}
}
var myClass = new MyClass;
// this will not compile,
// access modifier says private
// Set Field value
myClass._myField = 1;
// Get Property Value
var a = myClass.MyProperty;
// Set Property Value
myClass.MyProperty = 2;
// Get Property Value
var b = myClass.MyProperty2;
// this will not compile
// access modifier says private
// Set Property Value
myClass.MyProperty2 = 3;
// Call method that returns value
var c = myClass.GetMyMethod();
When you declare an auto-property in C#, it will get compiled behind the scenes into get and set methods, but you do not need to even think about those. You can access the property as if it were a field.
The benefit of having the property is that you can easily swap it out for a more conventional property with a backing field so that you can provide custom logic in the getter and/or setter. Until you need that, however, it is just extra noise. The auto-property provides the syntactic sugar to avoid that noise.
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Vector2 oldPos = brick.pos;
brick.pos = new Vector2.One;
Try changing to this:
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
And with C# you don't need this type of constructors. You can declare an object of this class without constructor with the following way:
public Brick brickTest(){
Speed = 10,
Position = new Vector2(),
Skin = new Texture2D()
};
namespace Clase_25_3_2014
{
class Brick
{
public int Speed { get; set; }
public Vector2 Pos { get; set; }
public Texture2D Skin { get; set; }
public Brick(int speed, Vector2 position, Texture2D skin)
{
this.Speed = speed;
this.Pos = position;
this.Skin = skin;
}
}
}
Using outside:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Console.WriteLine(Brick.Pos);
however, Behind the scenes the compiler create for each Property:
Private variable storage of the Property-type.
Two function (Get\Set).
Translate the property used to their Functions.
You should just do:
Brick brick = new Brick(1, Vector2.Zero, mySkin);
Vector2 vec = brick.pos;