GameObject[] is less accessible than (prefabs) - c#

This basically sets up the tiles for my endless runner, the error in particular being:
Inconsistent accessibility: field type 'gameobject' is less accessible than tilePrefabs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TileManager : MonoBehaviour {
private Transform playerTransform;
private float spawnZ = 0.0f;
private float tileLength = 39.0f;
private int amnTileOnScreen = 7;
public GameObject[] tilePrefabs;
// ** it is already public here **
private List<GameObject> activeTiles;
// Use this for initialization
public void Start () {
activeTiles = new List<GameObject> ();
playerTransform = gameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i <= amnTileOnScreen; i++) {
SpawnTile ();
}
}
private void Update () {
if (playerTransform.position.z > (spawnZ - amnTileOnScreen +
tileLength)) {
SpawnTile ();
//DisableTile ();
DestroyTile();
}
}
public void SpawnTile(int prefabIndex = - 1)
{
GameObject go;
go = Instantiate(tilePrefabs[0]) as GameObject;
// reappears here
go.transform.SetParent(transform);
go.transform.position = Vector3.forward*spawnZ;
spawnZ += tileLength;
activeTiles.Add (go);
}
private void DestroyTile()
{
Destroy (transform.GetChild (0));
}
}

Here is where the problem is located:
playerTransform = gameObject.FindGameObjectWithTag("Player").transform;
There is a difference between gameObject and GameObject. Notice the capitalized "G" in the second one.
GameObject is just a class used to create GameObjects.
gameObject is a variable that is created from GameObject as is declared as public GameObjects gameObject in Unity's Component class. It's simply an instance of GameObjects.
You get access to the gameObject variable when your TileManager script derives from MonoBehaviour.
The FindWithTag function is in the GameObject class and is also declared as static which means that you don't need instance of GameObject to call it. You have to call it directly with the class name as
Replace
gameObject.FindGameObjectWithTag("Player").transform;
with
GameObject.FindGameObjectWithTag("Player").transform;

Related

Trying to reference a public property from another class/object and it's always null

I have a C# script attached to an empty object called GameManager. The script is GameManager.cs. Here are the first few lines:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour {
public GameObject tilePrefab;
public GameObject startTile;
public GameObject lastTile;
I have another script attached to a camera called CameraManager.cs. I'm attempting to reference lastTile from GameManager but it's always null. Here is the full code for CameraManager.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraManager : MonoBehaviour
{
// Start is called before the first frame update
private Vector3 currPos;
private Vector3 newPos;
public float CamMoveSpeed = 5f;
GameManager gameManager;
private void Awake() {
gameManager = gameObject.AddComponent<GameManager>();
}
private void Start() {
}
private void Update() {
if (gameManager.lastTile != null) {
currPos = gameObject.transform.position;
Vector3 tilePos = gameManager.lastTile.transform.position;
newPos = new Vector3(currPos.x + tilePos.x, currPos.y + tilePos.y, currPos.z + tilePos.z);
this.transform.position = Vector3.Lerp(currPos, newPos, CamMoveSpeed * Time.deltaTime);
Debug.Log("Moving Camera...");
} else {
Debug.LogWarning("gameManager.lastTile is NULL");
}
}
}
This latest iteration is based on this SO question. The previous iteration was based on this SO question.
What is the proper way to reference a value from another script/class?
All I had to do was make the property public and then assign it in the inspector by dragging the GameManager object to the corresponding field. I was able to remove everything in the awake routine.
In the awake method of your camera class you assign gameManager with AddComponent, that will create a new instance of the GameManager script on the same object that your camera manager class is on, meaning that the reference that your camera manager class has to the GameManager class is not the one that’s on your GameManager gameObject.
What people do usually with GameManager classes is to use a Signlton structure
Basically you can have a static field of type GameManager in your GameManager class and assign it to its own instance on the Awake method of your GameManager class. Something like this
public class GameManager : MonoBehaviour {
public GameObject tilePrefab = new GameObject("Name");
public GameObject startTile = new GameObject("Name");
public GameObject lastTile = new GameObject("Name");
public static GameManager Instance;
private void Awake(){
Instance = this;
}
}
And then you can use your game manager in any other class with this line GameManager.Instance.lastTitle
public class GameManager : MonoBehaviour {
public GameObject tilePrefab;
public GameObject startTile;
public GameObject lastTile;
}
It doesnt look like your ever assigning these variables to anything, therefore they will always be null.
For them not to be null you need to assign them to something e.g.
public class GameManager : MonoBehaviour {
public GameObject tilePrefab = new GameObject("Name");
public GameObject startTile = new GameObject("Name");
public GameObject lastTile = new GameObject("Name");
}
Or you can set them dynamically by doing
private void Update() {
gameManager.lastTile = new GameObject("Name");
}

Assigning multiple prefabs to a script which only allows one to be added

I have a script where it puts an object (premade) onto a premade path using LeanTween which works fine.
The way this works is you can assign one object to the "path adder" (MoveController) that has the Moveable script attached to it.
However, I need to be able to add new prefabs made during runtime to the MoveController so they follow the path.
So how would I go about making instantiated objects attach to the MoveController during runtime.
Thank you.
Moveable script, also instantiates the prefabs
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity);
NextOnPath.AddComponent<Moveable>();
}
if (_destination.HasValue == false)
return;
if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0)
return;
_elapsedLerpDuration += Time.deltaTime;
float percent = (_elapsedLerpDuration / _totalLerpDuration);
Debug.Log($"{percent} = {_elapsedLerpDuration} / {_totalLerpDuration}");
transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent);
}
public void MoveTo(Vector3 destination, Action onComplete = null)
{
var distanceToNextWaypoint = Vector3.Distance(transform.position, destination);
_totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond;
_startPosition = transform.position;
_destination = destination;
_elapsedLerpDuration = 0f;
_onCompleteCallback = onComplete;
}
}
MoveController script
using System.Linq;
public class MoverController : MonoBehaviour
{
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
private void OnEnable()
{
_waypoints = GetComponentsInChildren<Transform>().ToList();
_waypoints.RemoveAt(0);
MoveToNextWaypoint();
}
private void MoveToNextWaypoint()
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
target.MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
target.transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Let me know if I need to clarify anything.
I understand this is quite a loaded question but I would greatly appreciate any help!
Solution
Moveable:
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
public List<Moveable> moveables = new List<Moveable>();
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Moveable NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
moveables.Add(NextOnPath.GetComponent<Moveable>());
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
}
}
}
MoveController:
public List<Moveable> targets;
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
public static MoverController instance;
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Note: Only the changed parts are displayed in the solution
Yes, if you use an Array then you can add as many prefabs as you want. You will still need to set the code in the script to handle multiple prefabs.
Although, it sounds like you might not understand the basics for C# and Unity and you might like to take a class, read a tutorial or watch a Youtube video.
Update
Because your question is so broad, I cannot just give you the code. That's something you should pay a coder for. But I will tell you the general ideas of what will need to be done.
In the Moveable class, you need to track your var NextOnPath after instantiating them. A great way to do this (As long as you don't plan to convert anything to JSON) would be with a List<>.
For example:
List<Moveable> moveables = new List<Moveable>();
void Update()
{
//Some code before - with instantiation
//It is best to track items by the script that you will use since you can just use moveables[i].gameObject to access the gameObject and moveables[i].transform to access transform
moveables.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}
You might save computing power by adding the Moveable script to your Electron prefab so you use:
Moveable nextOnPath = Instantiate(electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
Look up Singleton, you can use this to assign values to your MoverController class easily:
public static MoverController instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
Then in the Moveable class simply add it to MoverController
//Requires changing the above code
void Update()
{
//Some code before - with instantiation
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}

Proper way to "spawn" game objects. (I want to spawn projectiles)

Hey all I made the following class to act as "bullets" in my game and I don't know how to construct these bullet objects with the params that I want (determined at runtime) and spawn them in the game.
My first attempt looked like this:
if (canShoot())
{
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
but i get the following warnign from the unity editor:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
What is the proper way to spawn these bullets and to set the fields as I want them?
For more info here is the bullet class
public class Bullet : MonoBehaviour
{
private Rigidbody rigidbody;
private float wobble;
private float loft;
private float initialVel;
private Vector2 initialDirectionOffset;
private bool wobbleDirection = false;
private void Awake()
{
this.rigidbody = this.GetComponent<Rigidbody>();
}
private void Start()
{
transform.Rotate(initialDirectionOffset);
rigidbody.AddForce(initialVel*transform.forward);
rigidbody.AddForce(loft*transform.up);
}
private void FixedUpdate()
{
if (wobbleDirection)
{
rigidbody.AddForce(transform.right * wobble);
wobbleDirection = false;
}
else
{
rigidbody.AddForce(-transform.right * wobble);
wobbleDirection = true;
}
}
public Bullet setWobble(float wobble)
{
this.wobble = wobble;
return this;
}
public Bullet setLoft(float loft)
{
this.loft = loft;
return this;
}
public Bullet setInitialVel(float initialVel)
{
this.initialVel = initialVel;
return this;
}
public Bullet setDirectionOffset(Vector2 offset)
{
this.initialDirectionOffset = offset;
return this;
}
}
For more info here is the full "gun" class that spawns the bullets.
public class Gun : MonoBehaviour
{
private int FRAMES_BETWEEN_SHOTS = 10;
private int shotCoolDown = 0;
private const float LOFT = 100F;
private const float WOBBLE = 100F;
private const float INITIAL_VEL = 100F;
private Vector3 directionOffset;
private Recoil internalRecoil;
private void Awake()
{
internalRecoil = this.GetComponent<Recoil>();
if (internalRecoil == null)
{
throw new Exception("Could not find recoil component");
}
}
private void FixedUpdate()
{
if (shotCoolDown > 0)
{
shotCoolDown--;
}
}
private bool canShoot()
{
return shotCoolDown == 0;
}
public void performShoot()
{
if (canShoot())
{
//need to have a recoil component that reacts to bullet fire. then adjusts back to 0,0,0
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
}
}
My second attempt looks like:
GameObject.Instantiate(new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL)
.setDirectionOffset(internalRecoil.getRecoil()));
is this the right way to do it???
As 3Dave mentioned, the best way to do this would be to instantiate a prefab. Assuming you will be dealing with multiple projectiles, I like to separate this into a utility function for instantiating prefabs at a given position.
public static class UnityUtil {
public static GameObject instantiatePrefab(
Object prefab,
Vector3 position,
Transform? parent = null
){
// Create an instance of the prefab
GameObject instance = Object.Instantiate(prefab, position, Quaternion.identity) as GameObject;
// Set the parent
if(parent != null){
instance.transform.parent = parent;
}
return instance;
}
}
Then you could have code like
Transform projectileHolder;
GameObject myBulletPrefab;
Vector3 bulletPosition;
...setup variables...
var BulletGameObj = UnityUtil.instantiatePrefab(myBulletPrefab, bulletPosition, projectileHolder);
Bullet bullet = BulletGameObj.GetComponent<Bullet>();
I keep some other functions in my util for dealing with generic gameobject cases, you can see my full UnityUtil class here

How to assign multilpe GameObjects into one for Unity?

Basically I have a game where the player jumps from a truck to other trucks, and I want to reset my trucks position when the player dies so he can restart the level. Some of the irrelevant code such as the player movement I removed as it is not necessary for me to include. The code is like this so far:
using System;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Vector3 truckStart;
Vector3 startPoint;
//Assingables
public GameObject Truck;
public GameObject Player;
public GameObject Spawn;
public GameObject lava;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
startPoint = Spawn.transform.position;
truckStart = Truck.transform.position;
}
private void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.gameObject == lava)
{
Player.transform.position = Spawn.transform.position;
Truck.transform.position = truckStart;
}
}
How do I make my code so that instead of there being just 1 gameobject for my truck there is multiple? I've tried using tags to find gameobjects but when I do that it won't let me reset its positions.
Not sure what you want, but It seems it's not that difficult. As you already know how to assign serializable values in Unity via inspector, just declare an array of Truck, like:
private Vector3[] truckStartPoints;
public GameObject[] Trucks;
And, as a general Unity coding tip, the following is better (unless you have to access it from other class):
[SerializeField]
private GameObject[] trucks;
This makes trucks not accesible from other code, but still can be serialized via inspector.
And initialize like this:
private void Start()
{
truckStartPoints = new Vector3[trucks.Length];
for (int i = 0; i < trucks.Length; i++)
truckStartPoints[i] = trucks[i].transform.position;
}
And reset like this:
private void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.gameObject == lava)
{
Player.transform.position = Spawn.transform.position;
for (int i = 0; i < trucks.Length; i++)
trucks[i].transform.position = truckStartPoints[i];
}
}

How can i change each gameobject movement speed?

In the Hierarchy i have 2 ThirdPersonController.
In the Window > Animator i created new empty state called it Walk and set it to HumanoidWalk so when running the game both players are walking.
On one of them i added the script and as Prefab the second ThirdPersonController(1).
Then when running the game it's making clones of the ThirdPersonController(1).
So i see in the Hierarchy more N ThirdPersoncontrollers.
Today to change the speed of walking for each ThirdPersonController i change in the Inspector the Move Speed Multiplier.
But if i want in the script already when creating the clones to set to each one another speed how can i do it ?
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
void Awake()
{
gos = new GameObject[NumberOfObjects];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos [i] = clone;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
What i tried now is to get the Animator component of the Prefab and set the speed to all the clones:
using UnityEngine;
using System.Collections;
public class Multiple_objects : MonoBehaviour {
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
private Animator _animaotr;
void Awake()
{
gos = new GameObject[NumberOfObjects];
for(int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos [i] = clone;
_animaotr.speed = 10;
}
}
// Use this for initialization
void Start () {
_animaotr = prefab.GetComponent<Animator> ();
}
// Update is called once per frame
void Update () {
}
}
But the main problem is that on my first ThirdPersonController in the Hierarchy the original one i created in Window > Animator empty state called it Walk and set HumandoidWalk.
Now to set the speed for some reason changing the Animator speed never effect of anything for example:
_animaotr.speed = 10;
Only when changing the speed in the ThirdPersonController > Inspector > Third Person Character (Script) > Move Speed Multiplier. And it's changing the same speed to all ThirdPersoncontrollers in the Hierarchy including this i clone.
But how do i change each clone speed to another vlaue of speed ? And why the _animator.speed not changing anything and i need to use this Move Speed Multiplier ?
The Move Speed Multiplier property that is showing in the Editor is declared as m_MoveSpeedMultiplier in the ThirdPersonCharacter script. It is delacre as float m_MoveSpeedMultiplier = 1f; which means that it is a private variable and cannot be accessed from another script. The reason it is showing up in the Editor is because it has [SerializeField] on top of it which means that it is a serialized private variable.
To access it during run-time, you have to change from float m_MoveSpeedMultiplier = 1f; to public float m_MoveSpeedMultiplier = 1f; in the ThirdPersonCharacter script.
Use GetComponent to get instance of ThirdPersonCharacter from the gos GameObject then save it somewhere for re-usual. Since you have 2 ThirdPersonCharacter, you can create two ThirdPersonCharacter arrays to hold those instances. It should look like the code below:
using UnityEngine;
using System.Collections;
using UnityStandardAssets.Characters.ThirdPerson;
public class Multiple_objects : MonoBehaviour
{
public GameObject prefab;
public GameObject[] gos;
public int NumberOfObjects;
private ThirdPersonCharacter[] thirdPersonCharacter;
void Awake()
{
thirdPersonCharacter = new ThirdPersonCharacter[2];
gos = new GameObject[NumberOfObjects];
for (int i = 0; i < gos.Length; i++)
{
GameObject clone = (GameObject)Instantiate(prefab, Vector3.zero, Quaternion.identity);
gos[i] = clone;
thirdPersonCharacter[i] = clone.GetComponent<ThirdPersonCharacter>();
}
}
// Use this for initialization
void Start()
{
thirdPersonCharacter[0].m_MoveSpeedMultiplier = 5f;
thirdPersonCharacter[1].m_MoveSpeedMultiplier = 5f;
}
// Update is called once per frame
void Update()
{
}
}

Categories

Resources