So I'm trying to make a 2D Snake game in Unity, which is made from 4 different C# scripts.
In this particular I try to delete the Food which the snake eats but nothing happens (Its marked in the Code), and the other function works just fine. I don't even get an Error.
If you need the other scripts, I can send those as well.
public void SnakeMoved(Vector2Int snakeGridPosition)
{
if (snakeGridPosition == foodGridPosition)
{
Object.Destroy(foodGameObject);
SpawnFood();
}
}
I tried to see if the rest in the IF clause works and the rest is working perfectly. I don't know what I did wrong.
Code for the foodGameObject:
private void SpawnFood()
{
foodGridPosition = new Vector2Int(Random.Range(0, width), Random.Range(0, height));
GameObject foodGameObject = new GameObject("Food", typeof(SpriteRenderer));
foodGameObject.GetComponent<SpriteRenderer>().sprite = GameAssets.i.foodSprite;
foodGameObject.transform.position = new Vector3(foodGridPosition.x, foodGridPosition.y);
}
In Unity you cannot create game objects in code using new GameObject(...). Instead you need to instantiate these, like that:
GameObject instantiatedFood = Instantiate(new GameObject("Food", typeof(SpriteRenderer)));
Instead of new GameObject("Food", typeof(SpriteRenderer)) you could also pass a prefab here which you link in the editor.
This instantiation step makes sense because you can prepare and deal with GameObjects in code before actually spawning them in the world.
Related
I'm making an App that instantiates a certain model when the camera is pointed to a QR.
The app then comunicates to a website and shows the data of a place related to the model (example: botanic garden, it shows some flowers and a text about ecology)
Thing is, while I can use text from a website using JSon, I can't seem to find a way to do the same.
I also have a code that makes a JSon, this one returns the name of the model.
My question is, is there a way to use that name to search the prefab in my assets and thus instantiate the prefab?
yes, it is possible, create a dictionary (string, GameObject) and a list for the inspector with its prefabs, use the list to create a new dictionary
Private void Awake(){
dictionary = new Dictionary (string,GameObject); // <- (don't use parentheses)
foreach ( GameObject go in yourList ) {
dictionary.Add(go, go.name);
}
Private void Search(string name){
if(dictionary.ContainsKey ( name )){
Instantiate(dictionary[name]);
}
}
Workaround, assign prefabs in editor, in your script have references to different prefabs:
public GameObject prefab1;
public GameObject prefab2;
When you want to instantiate your prefab
switch (JSONstring) {
case "Garden":
Instantiate(prefab1, new Vector3(0, 0, 0), Quaternion.identity);
break;
}
I have a Script in my main Assets folder called BezierWalk.cs
In another script, Spawn, I'm trying to instantiate objects from Prefab Sphere and attach BezierWalk.cs to them.
Spawn script:
public class Spawn : MonoBehaviour
{
public GameObject Sphere;
//string ScriptName = "BezierWalk";
void Start()
{
Vector3 vSpawnPos = transform.position;
for (int i = 0; i < 20; i++)
{
var objectYouCreate = Instantiate(Sphere, vSpawnPos, transform.rotation);
//objectYouCreate.AddComponent<T>("Assets/BezierWalk.cs");
//objectYouCreate.AddComponent(typeof(ScriptName)) as ScriptName;
//objectYouCreate.AddComponent<ScriptName>();
//var myScript = Sphere.AddComponent<BezierWalk.cs>();
vSpawnPos.z += 20;
}
}
You can see commented out attempts...
How I am supposed to do this properly? Thank you.
If you look at how you reference components in unity, the answer should be clear - did you try any of the ones you listed?
Reference: https://docs.unity3d.com/ScriptReference/GameObject.AddComponent.html
The normally used is as it is easy reading
objectYouCreate.AddComponent<ClassName>();
You can use
objectYouCreate.AddComponent(typeof(ClassName));
the .cs is for humans, its your readable version. so you would never need the .cs reference in your code.
note: I mentioned it as ClassName rather than scriptname, as while they are the same in monobehaviors in unity, it isnt the same anywhere else in c# so, the important bit is not the name of the file you made, but the name of the class within it.
Another way is to have prefabs, make a prefab of your object with all the components you need already on it.
If your script lies within certain namespace, you should follow the following format
GameObject.AddComponent(typeof(namespace.className));
I want to create a game in Unity C# in which the player will add as much as drones he needs as game objects to the game world. Therefore, I made a drone prefab and later in runtime I ask the number of drones the player wants to have (e.x. n numbers) and instantiate the prefab n times.
The script which is attached to the drone prefab is called drone_script.
Therefore, I am having a drone_script class as a general class. This class has an attribute (let's call it subscriber) which should have different unique values for each drone. So, it is created as a null reference in drone_script general class and later in runtime, I will initialize it.
During runtime, I create n (the same numbers as the drone game objects) objects from this class and assign their subscriber attribute different values.
This is how it looks:
some_class
{
for (int i = 0; i < number_of_Drones; ++i)
{
drones_script[i] = new drone_script();
\\ here I am creating n objects of my general drone_script class
drones_script[i].drone_subscriber = a unique value;
\\ here I am assigning to each of the drone objects' drone_subscriber attribute a different value.
drco = new Vector3((float)0, (float)1, (float)0);
drones[i] = Instantiate(Resources.Load("drone"), drco,
Quaternion.Euler(-90, 0, 90)) as GameObject;
\\I instatiate the drone prefab in unity game world here.
}
}
drone_script
{
public ROSBridgeSubscriber drone_subscriber;
void Start() { \\some code }
void FixedUpdate () { \\some code }
}
My problem is when the drone game objects are created in unity, the general class drone_script is attached to them and because of this their subscriber attribute is null. However, I need the drone_script[i] object to be assigned to them as their script, not the general drone_script itself. Because only then each drone game object has its corresponding subscriber attribute value from before even the start function is being called.
Am I thinking correctly?
If yes, how can I do it?
First of all, I suggest you to assign dronePrefab using editor or in Start method of "some_class" because Resources.Load("drone") might be resource consuming, since you load a drone resource "n" times.
Now your specific problem, you don't need to create new instances of "drone_script" because as you mentioned yourself, each drone that you create has his own "drone_script". So basically what we need to do is:
Instantiate drone and save his reference (as GameObject)
Get that drone's "drone_script" (using GetComponent<>(); )
Save that script reference in our array so we can use it later
Adjust some variables of that script now or later in the game
Something like that should work. In code everything should be something like this (didn't test, might be some miss types)
some_class
{
public GameObject dronePrefab;
for (int i = 0; i < number_of_Drones; ++i)
{
// Step 1.
drco = new Vector3((float)0, (float)1, (float)0);
drones[i] = Instantiate(dronePrefab, drco,
Quaternion.Euler(-90, 0, 90)) as GameObject;
// Steps 2 & 3
drones_script[i] = drones[i].GetComponent<drone_script>();
// Step 4
drones_script[i].drone_subscriber = a unique value;
}
}
EDIT:
Just to point it out: drone_script has to be attached to drone prefab
Recently took an online course on beginner c# w/ unity and unfortunately the instructor ended the course with a broken and unfinished code. Having about 3 weeks of experience under my belt, I decided to learn, research, and make a viable build before moving on.
I've got two lists and a variable:
private List<Widget> WidgetList = new List<Widget>();
private List<Collider2D> ItemList = new List<Collider2D>();
private Collider2D itemSpot;
I then have an if statement inside the update method that, upon clicking the left mouse button the code changes the tag of the item clicked, adds that item to a ItemList, and generates a new Widget in that location.
if (Input.GetMouseButtonDown (0)) {
Vector2 worldPoint = Camera.main.ScreenToWorldPoint (Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast (worldPoint, Vector2.zero);
if (hit.collider.tag == "BlankItem") {
itemSpot = hit.collider;
itemSpot.tag = "FullItem";
RegisterItemSpot (itemSpot);
MakeWidget (hit);
}
}
This currently works fine, albeit I get the impression it was not the most ideal code (was not a well made course). The call to the RegisterItemSpot and MakeWidget methods work flawlessly, each properly uses the .Add to their respective lists. At the end of the game, I've got a method that also clears both lists without a problem.
Here is my hurdle: If I destroy a single instance of a Widget, I would like the itemSpot that was associated with it to change its tag to "BlankItem" again. I used a similar function to the previous to destroy the Widget:
void Update (){
if (Input.GetMouseButtonDown (0)) {
Vector2 worldPointt = Camera.main.ScreenToWorldPoint (Input.mousePosition);
RaycastHit2D hitt = Physics2D.Raycast (worldPointt, Vector2.zero);
if (hitt.collider.tag == "Tower" && destroyMode != false)
Destroy(hitt.transform.gameObject);
}
}
From there in the OnDestroy of the Widget class i have it remove itself from the list, and that works fine too.
In my head I imagine a couple lines of code that, within the OnDestroy of the Widget, would rename the itemSpot that was initially associated with that specific destroyed Widget. I don't know how to associate the new Widget with the specific itemSpot. Something along the lines of "if this object is destroyed, then the object associated with THAT collider gets retagged". The few ideas I have would require rebuilding this code from the ground up (maybe making arrays instead of lists?)and I feel like my novice brain would be better served just moving on to my next course instead of taking all that time. Maybe not? I just need some advice from more experienced folks. If i need to provide more information, please let me know (10 classes of hundreds of code seemed like too much to just throw on here).
EDIT: Ive included the MakeWidget() method and the RegisterItemSpot() and RegisterWidget() methods. I should have from the get-go, very sorry!
public void MakeWidget (RaycastHit2D hit){
if (!EventSystem.current.IsPointerOverGameObject) {
Tower newWidget = Instantiate (widgetBtnPressed.WidgetObject);
newWidget.transform.position = hit.transform.position;
RegisterWidget (newWidget);
widgetBtnPressed = null;}
}
public void RegisterItemSpot (Collider2D itemSpot){
ItemList.Add(itemSpot);
}
public void RegisterWidget (Widget widget)
{
WidgetList.Add(widget);
}
I think your best approach is creating a new containerclass, and then declaring a new list of said class. Here is an example
public class WidgetContainer()
{
public Widget widget;
public Collider2D collider;
}
Then replace your two lists with this one
public List<WidgetContainer> widgetContainers = new List<WidgetContainer>();
Then merge your makewidget and registeritemspot method into one method and create and add a new WidgetContainer to the list. Now you will always have a connection stored between these two variables.
var newContainer = new WidgetContainer
{
widget = newWidget,
collider = itemSpot
}
Finally in your OnDestroy() of the said widget as you describe, you can do this:
widgetContainers.First(x => x == this).collider.tag = "your tag here";
"this" references to instance of the class. In your case this would be your Widgetscript.
Do not that you will need to add this using statement at the top of your script for Linq.
using System.Linq;
I'm having some discrepancy between playing my unity project inside the editor, and playing the build it makes.
Inside the editor, when I play my game I have code that spawns enemies when my player hits a target spot, which works. The enemies spawn and do what they need to do. However, when I build the project and run it, my player hits the same spot, but nothing appears.
I ran the project in the development mode and it claims I have not set the reference which I thought I had. I mean, if it didn't work, why is it working in the editor?
To try and combat this, I re-imported the assets I'm using. I remade the enemy spawn points. But I still get the same issue.
Has anyone ever came across this before?
If so, how can I fix this issue when it works inside the editor, but not in the build?
Here is my spawning function:
public GameObject SpawnEnemies()
{
Vector3 _position = new Vector3(transform.position.x, transform.position.y, transform.position.z);
// instantiate particel system
// Instantiate(_particle, _position, Quaternion.identity);
audio.PlayOneShot(_appearSound);
_clone = (GameObject)Instantiate(_enemy, _position, transform.rotation);
_ai = _clone.GetComponent<HV_BaseAI>();
_ai._waypoints = _wayPoints;
return _clone;
}
This is controlled by my section class which has the following function:
public List<GameObject> SpawnGroundEnemies()
{
List<GameObject> groundObjectList = new List<GameObject>();
for (int i = 0; i < _spawnPoints.Count; i++)
{
groundObjectList.Add(_spawnPoints[i].SpawnEnemies());
}
return groundObjectList;
}
Which, in turn is controlled by my section controller:
GameObject g, f;
g = GameObject.FindGameObjectWithTag("SectionController");
f = GameObject.FindGameObjectWithTag("SectionController");
HV_SectionController tempSectionControllerGroundEnemies, tempSectionControllerFlyingEnemies;
tempSectionControllerGroundEnemies = g.GetComponent<HV_SectionController>();
tempSectionControllerFlyingEnemies = f.GetComponent<HV_SectionController>();
_groundEnemiesRemaining = tempSectionControllerGroundEnemies._numberOfGroundEnemies.Count;
_flyingEnemiesRemaining = tempSectionControllerFlyingEnemies._numberOfFlyingEnemies.Count;
_enemiesRemaining = _groundEnemiesRemaining + _flyingEnemiesRemaining;
if (!_moving)
{
if (_enemiesRemaining == 0)
{
MoveToNextSection();
}
}
if (_moving)
{
if (Vector3.SqrMagnitude(_camera.transform.position - _camNavMesh.destination) < 5.0f)
{
_moving = false;
// spawn the next set of enemies
// need this for each section of the game
// without this, no enemies will spawn
if (CurrentSection == 1)
{
_numberOfGroundEnemies = _sections[0].SpawnGroundEnemies();
_enemiesRemaining = _numberOfGroundEnemies.Count;
}
if (CurrentSection == 2)
{
_numberOfFlyingEnemies = _sections[1].SpawnFlyingEnemies();
_enemiesRemaining = _numberOfFlyingEnemies.Count;
}
if (CurrentSection == 3)
{
_numberOfGroundEnemies = _sections[2].SpawnGroundEnemies();
_enemiesRemaining = _numberOfGroundEnemies.Count;
}
}
}
}
private void MoveToNextSection()
{
if (_currentSection == _sections.Count)
{
_currentSection = 0;
}
_camNavMesh.SetDestination(_sections[_currentSection]._cameraTransform.position);
_lookAtPoint.SetTimer(_sections[_currentSection]._cameraLookAtTarget.position);
_moving = true;
_currentSection += 1;
}
Can anyone see anything i'm doing wrong?
Update
Since this post, I've tried reimporting all of my assets again. I've even created a fresh project and done a fresh build that way, but I still have the same issue. Also, to help, I've take two screen grabs to show what I mean.
First off, the game running in the unity editor:
As you can see, there is 4 skeletons on the bridge. These guys appear when I hit play in the unity editor.
Now, when I build the project, this is same sections:
As you can see, no enemies, but when its a development build, it says I've no reference set. In the output log unity genereates, it gives me this message:
NullReferenceException: Object reference not set to an instance of an
object at HV_SpawnGroundEnemy.SpawnEnemies () [0x00052] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Enemy Scripts\HV_SpawnGroundEnemy.cs:37
at HV_Section.SpawnGroundEnemies () [0x0000d] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Helper Scripts\HV_Section.cs:26
at HV_SectionController.Update () [0x000c2] in
C:\Users\sean.obrien\Desktop\Medieval Darkride
Level\Assets_scripts\Helper Scripts\HV_SectionController.cs:72
Again, this only appears in the built version of the game. When I run this inside Unity, everything works fine. I honestly have no idea why this issue arises when it works fine in the editor but not in the build.
There's most probably two cases here. Either you've not saved your scene yet, or you're not including the correct scene while making the build.
If it's the first case, just save the scene. If it's second, then you need to choose proper scene in the build settings and then rebuild.
Explanation:
You might have saved the scene as some other file, but the build setting does not update the scene file dynamically. It'll still point to the old scene. So you just need to remove the old scene and add the current scene you're using.