Multiple references to a Ball object in Unity - c#

I am making a game like pong with multiple power-ups. One of them is to have a "Triple Ball" power-up. I tried just creating another reference of the ball, but that doesn't seem to work. I figured this would work because I mostly work with Java where I can just create another instance. Here is how I tried to do it, I tried testing through just clicking a button:
public class TripleBall : MonoBehaviour {
public Ball firstBall;
private int amountOfBalls = 2;
private Ball[] ballArray;
private bool start, avail, located;
void Start () {
ballArray = new Ball[amountOfBalls - 1];
for (int i = 0; i < ballArray.Length; i++)
ballArray[i] = new Ball();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.B))
start = true;
if (start)
{
//Begin locator: Makes it so when the balls spawn they spawn where the first ball
if (!located) {
for (int i = 0; i < ballArray.Length; i++)
{
ballArray[i].transform.position = firstBall.transform.position;
}
located = true;
}
//End locator : if statement and loop
}
}
}
But it seems I can't do this because Ball is of type MonoBehavior.
Question: Is there any way to do this the way I planned or any way at all? Thanks in advanced!

You really don't use new to create an instance. The only place you are right in your code is using new to initialize array. After that, you are supposed to use GameObject.AddComponent to create a new instance instead of the new keyword. This is because Ball class derives from the MonoBehaviour.
You Ball class derives from MonoBehaviour.
public class Ball : MonoBehaviour
{
}
Here is your code fixed:
private Ball[] ballArray;
void Start()
{
ballArray = new Ball[amountOfBalls - 1];
for (int i = 0; i < ballArray.Length; i++)
ballArray[i] = gameObject.AddComponent<Ball>();
}
Assuming that your Ball class looks like the code below and does not inherit from MonoBehaviour:
public class Ball
{
}
Then code in your question would be valid and using new is the right way.

You need to use instantiate. It is derived from Unity's Object class, so assuming "ball" inherits from either Object or GameObject, this is what you want. You can find more info about it here: http://docs.unity3d.com/ScriptReference/Object.Instantiate.html

Related

Disable Child Element of a Prefab with Script

I have just started to learn Unity 2d and I have taken a task at hand where I want to Instantiate a Prefab from Assets folder and then disable some of the child elements in it once the prefab is initiated. Following is my code :
void createPuzzleGame()
{
Puz = Resources.Load("Dog") as GameObject;
Instantiate(Puz, PuzArea.transform);
for (int i = 0; i < Puz.transform.childCount; ++i)
{
Transform currentItem = Puz.transform.GetChild(i);
if (currentItem.name.StartsWith("a") || currentItem.name.StartsWith("og"))
{
currentItem.gameObject.SetActive(false); //this line doesn't work
}
else
{
Debug.Log(currentItem.name);
}
}
}
I want to disable all child images of the prefab Puz that start with letter 'a' or 'og'.
The prefab Dog(clone) gets created on running the code. However the child elements don't seem to disable. Where am I going wrong? Please help.
You are trying to iterate through the children of the original prefab Puz.
You rather need to iterate through the newly created instance
var instance = Instantiate(Puz, PuzArea.transform);
foreach(Transform child in instance.transform)
{
if (child.name.StartsWith("a") || child.name.StartsWith("og"))
{
child.gameObject.SetActive(false);
}
else
{
Debug.Log(child.name);
}
}
Puz is the prefab's reference. Instantiate returns the actual instance you create from Puz. You need to change the instance:
Transform dog = Instantiate(Puz, PuzArea.transform).transform;
for (int i = 0; i < dog.childCount; ++i) { /* ... */ }
or, you can add a script to your prefab, and move this logic there, something like this (in this case, you have to add your references to _images on the prefab, but you can use your search by name logic also):
public class Puzzle : MonoBehaviour
{
[SerializeField] private GameObject[] _images;
public void SetImagesVisibility(bool visible)
{
for (int i = 0; i < _images.Length; i++) _images[i].SetActive(visible);
}
}
and you can use this at instantiating:
Puzzle puz = Instantiate(Puz, PuzArea.transform).GetComponent<Puzzle>();
puz.SetImagesVisibility(false);

Gameobject destroy fails inside destructor

I have an object, which contains a list of GameObjects. I wish to destroy all of these GameObjects in its destructor.
However, when I attempt to call GameObject.Destroy() from inside the destructor, it seems to halt execution (the line after GameObject.Destroy() never executes, but the line before it does)
If i copy and paste exactly the same code into a function called not_a_destructor() and call that instead, it works perfectly. What gives? I've got it working, but I would really like to understand what's going on.
Destructor and not_a_destructor() code:
// Destructor DOES NOT work
~MoveAction(){
for(int i = 0; i < arrows.Count; i++){
Debug.Log("wasd");
GameObject.Destroy(arrows[i]);
Debug.Log("asdf");
}
}
// Identical code, calling not_a_destructor() works perfectly
public void not_a_destructor(){
for(int i = 0; i < arrows.Count; i++){
Debug.Log("PRETEND DESTRUCTOR!");
GameObject.Destroy(arrows[i]);
Debug.Log("GameObject destroyed successfully");
}
}
As requested in comments, a full copy of the class:
public class Action
{
public int type;
public string debug_string;
public GameObject ui_pill; // Only present for Actions created on the client
}
public class MoveAction : Action
{
public int type = ActionType.MOVE;
public MapHex origin;
public List<MapHex> route; // Intermediate hexes travelled through during the move (includes target_hex)
public Fleet fleet;
private List<GameObject> arrows = new List<GameObject>(); // Arrows for the graphical representation of the pending move on the tactical map
public MapHex target_hex {
get {
return route[route.Count - 1];
}
}
public string debug_string {
get {
return "MOVE ACTION WITH FLEET: " + fleet.name;
}
}
public MoveAction(Fleet _fleet, List<MapHex> _route){
fleet = _fleet;
route = _route;
origin = fleet.planned_position;
update_arrows_from_route();
}
public void update_arrows_from_route(){
Material default_material = new Material(Shader.Find("Sprites/Default"));
// Create one arrow for every hex we will pass through.
MapHex last = fleet.planned_position;
foreach (MapHex hex in route){
// Create arrow from last to hex
GameObject arrow_gameobj = new GameObject();
arrow_gameobj.name = "move_order_arrow";
LineRenderer line_renderer = arrow_gameobj.AddComponent<LineRenderer>();
line_renderer.material = default_material;
line_renderer.SetColors(fleet.owner.color, fleet.owner.color);
line_renderer.positionCount = 2;
arrow_gameobj.layer = layers.tactical_map;
Vector3[] line_points = new Vector3[]{last.position, hex.position};
line_renderer.SetPositions(line_points);
line_renderer.startWidth = 0.1f;
line_renderer.endWidth = 0.1f;
arrows.Add(arrow_gameobj);
last = hex;
}
}
public void not_a_destructor(){
for(int i = 0; i < arrows.Count; i++){
Debug.Log("PRETEND DESTRUCTOR!");
GameObject.Destroy(arrows[i]);
Debug.Log("GameObject destroyed successfully");
}
}
~MoveAction(){
for(int i = 0; i < arrows.Count; i++){
Debug.Log("wasd");
GameObject.Destroy(arrows[i]);
Debug.Log("asdf");
}
}
Its probable best to use more of Unity and less of C#, there is a good callback called OnDestroy() which would be a fine place to destroy all the arrows. If execution of your unity code depends on running a finalizer on something, this is a very strong code smell.
Unless you are using IO in a way that REQUIRES an action to happen in a finalizer (possibly things like releasing an IO resource), its best to leave them empty, and put Unity code inside Unity callbacks

Variables in other scripts in Unity

I am trying to create a game in Unity 2d. I have finished most of what I want to do and have moved on to the enemies. The enemies (dragons) come in from different points of screen. To do this I have placed sprite game objects where I want the dragon to spawn. I have made all of these objects a child of another object called DragonAncores. I attached a script to DragonAncores which says this...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonTracker : MonoBehaviour {
// is gold dragon in play?
public bool GoldDragonInit = false;
// curently active dragons
public int DragonCount = 0;
// defalts to 5
public int Difficulty = 5;
}
I am then attaching a script to each sprite which will eventually summon in a dragon Prefab (containing 2 colliders and an animator) biased of If statment logic derived from the other variables.
Below is the code I am using.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2)
DragonType=RNG.Next(0,101)
if (RSpawn = 1) ;
{
if (LiveDragons > DifLev) ;
{
if (DragonType > 99) ;
{
// summon regular dragon
}
if (DragonType = 100) ;
{
if (GoldDragonSpawn = true) ;
{
// summon gold dragon
}
}
}
}
}
}
This is throwing up this error list.
This shows my hierarchy in unity and the anchor points (the Squair crosshair looking things)
I have looked for other threads that adress this topic and they all try different methods, none work.
I am using Unity 2018.2.18f1
There are a few errors in your code here. The following is incorrect.
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
The correct way to access this, seeing as you said DragonAncors is the parent would be:
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
This sets the GoldDragonInit Boolean to the value of GoldDragonSpawn. This has to be inside a function, as you have it outside of a function I presume you needed this set on start. Therefore I have placed it in the void Start() function. This is called at the start of the game(loaded scene).
You also do not need semi-colons ; after an if statement, however it does need to appear after every line of difinitive code. The code you have provided should instead look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dragons : MonoBehaviour {
// same as GoldDragonInit
bool GoldDragonSpawn = false;
// same number as DragonCount in DragonTrackeer
int LiveDragons;
// same as Difficulty
int DifLev;
void Start()
{
// variables from other script
GetComponentInParent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
System.Random RNG= new System.Random();
void update()
{
RSpawn=RNG.Next(0,2);
DragonType=RNG.Next(0,101);
if (RSpawn = 1)
{
if (LiveDragons > DifLev)
{
if (DragonType > 99)
{
// summon regular dragon
}
if (DragonType = 100)
{
if (GoldDragonSpawn = true)
{
// summon gold dragon
}
}
}
}
}
}
This works because DragonTracker is a script in the objects parent. If this was not the case then GetComponentInParent().GoldDragonInit = GoldDragonSpawn; would be replaced like so:
[SerializeField]
private GameObject DragonAncors;
void Start()
{
DragonAncors.GetComponent<DragonTracker>().GoldDragonInit = GoldDragonSpawn;
}
This is not valid c# code:
//get cariables from other script
DragonAncors.cs.GetComponent.<DragonTracker>() GoldDragonInit = GoldDragonSpawn;
Why? Because it isn't inside a method.
Also, the comment is wrong. It isn't getting a variable (typo, too), its setting a variable in another script!
The reason for the first...16 problems Visual Studio is complaining about are because of this line.
At this location you are only allowed to declare methods, fields, and properties and you're currently trying to access another class and change one of its members (something you can only do inside a method).
Additionally, you have .cs which I assume is because "DragonAnchors.cs is the file name!" which you don't need to do. I'm not sure how to go about rewriting this line (inside Start()) as I'm not sure what you're trying to actually do. That is, I don't know where an instance of DragonAnchors actually resides. You're calling GetComponent(), which is typically reserved for accessing components attached to game objects, but as you've attempted to do so on a static class reference, I'm not sure if you meant to call it on this or on something else.
This is how you can get to DragonTracker:
DragonTracker dt = GameObject.Find("DragonAncores").GetComponent<DragonTracker>()
Debug.Log(dt.DragonCount);
There are a lot of errors there and they may take some steps to go through, but first things first you should clear up the issue with the code you're trying to use being unsupported. Go into the project settings and change the compiler language version as it notes on the 5th error down. This should allow you to use the newer functionality.

Scripts Works on one scene but error in another [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 years ago.
So I'm getting an error which says
Object reference not set to an instance of an object Explode.LateUpdate () (at Assets/Scripts/Explode.cs:40)
I'm not sure why I get this error as everything works fine and I have the exact same script in a different scene without the error message. I'd love any help please.
I'm exploding my player with different coloured boxes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Explode : MonoBehaviour {
[System.Serializable]
public class ExplodeColours
{
public Color32[] Colours;
}
public static bool explode, explodeOnce;
public ParticleSystem Explodes;
private ParticleSystem explosionSystem;
public Transform player;
public List<ExplodeColours> ColoursList;
// Use this for initialization
void Start () {
PlayerPrefs.SetInt("SelectedChar", 0);
PlayerPrefs.Save();
explodeOnce = false;
explode = false;
}
// Update is called once per frame
void Update () {
if (explode == true)
{
explodeOnce = true;
PlayerExplode();
}
}
void LateUpdate()
{
if (explode)
{
ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[explosionSystem.main.maxParticles];
//ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[Explodes.main.maxParticles];
int NumParticlesAlive = explosionSystem.GetParticles(Particles);
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i].startColor = (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
explosionSystem.SetParticles(Particles, NumParticlesAlive);
explode = false;
}
}
void PlayerExplode()
{
explosionSystem = Instantiate(Explodes, player.position, player.rotation);
explosionSystem.Play();
}
}
If this is line 40 (I had to count):
ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[explosionSystem.main.maxParticles];
Then explosionSystem or main or maxParticles is null. I'm going to close this as a duplicate, this error is so common and the way to resolve it is easy.
But a break point on the line of code, when the code control halts, check which object is null and make sure its set.
EDIT:
Use the GetComponent to initialize the explosionSystem variable in the Start function.First, find the GameObject the ParticleSystem is attached to the use GetComponent to get the ParticleSystem.
private ParticleSystem explosionSystem;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectParticleSystemIsAttachedTo");
explosionSystem = obj.GetComponent<ParticleSystem>();
}
Also, inside the for loop, you tried to use Particles's startColor property without initializing each one. You can do that with Particles[i] = new ParticleSystem.Particle();.
Change:
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i].startColor
= (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
to
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i] = new ParticleSystem.Particle();
Particles[i].startColor
= (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
To avoid the null reference error, make sure all variables (which are using) has values other wise at least assign default values in constructor level

UNITY c#: Spawning character prefabs based on user selection

I have game for multiple players where each user selects their hero before game starts and that loads the selected heroes into the battle arena.
I have small issue with getting the instantiation to spawn in correct numbers of players
The method that I have for Spawning the characters:
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
This script checks for amount of selected hero cards and puts it against my list that has all the available heroes to choose from(prefabs).
The debug.log shows that only the correct heroes get called.
Instantiate ends up spawning a loot of heroes instead of the selected amount.
For clarity I attach full class:
{
private int playerSize; //amount of choices for card selection
private GameManager GM;
[Header("Lists for Spawning in Heroes")]
public List<GameObject> SelectedCards;
public List<GameObject> AvailableHeroes;
public List<Transform> PlayerSpawnLocation;
[Header("Canvas used for the game")]
public Transform GameCanvas;
public Transform CharacterCanvas;
//When scene starts it takes how many players will be picking a card.
void Start()
{
//connects this script with gamenmanager to be able to manipulate the cameras
GM = GameObject.Find("GameManager").GetComponent<GameManager>();
//gets playersize information from main menu selection
PlayerPrefs.GetInt("PlayerSize");
playerSize = PlayerPrefs.GetInt("PlayerSize");
SelectedCards = new List<GameObject>();
//enables/disables correct canvas not to cause any problems when we initiate this scene
GameCanvas.gameObject.SetActive(false);
CharacterCanvas.gameObject.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (playerSize <= 0)
{
Placement();
GM.CharacterSelectionCamera.enabled = false;
GameCanvas.gameObject.SetActive(true);
CharacterCanvas.gameObject.SetActive(false);
GM.BattleCamera.enabled = true;
}
}
public void PlayerSelected(int cardPicked)
{
playerSize -= cardPicked;
}
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
}
I hope someone can explain where I am going wrong with this.
Thanks,
I got the answer, I guess I was just being tired from working and could not see the obvious.
For those who wonder what solution is
The method gets called each frame thus it continues to endlessly spawn objects
There are 2 ways to fix it
1 Make coroutine and then return after you make your initial batch
2 Use a boolean at update so not only it checks player size but also whenever it can spawn it or not, you set the boolean to false after method get called.
I did not even notice the update function part.
Just a heads up, in your start function, PlayerPrefs.GetInt("PlayerSize"); is not doing anything since the value is not saved anywhere.

Categories

Resources