I'm pretty sure that
animation.Play("DoorOpen");
Would play the animation "DoorOpen", but when i'm trying to put it in my code, it just giving me an error message:
The Animation attached to this GameObject (null if there is none attached).
using UnityEngine;
using System.Collections;
public class DoorPhysics : MonoBehaviour {
int Open = 0;
// Update is called once per frame
void Update() {
if (Open == 0) {
if (Input.GetKeyDown("e")) {
animation.Play("DoorOpen");
}
}
}
}
You need to show location of gameobjects in unity, they do not know eachother, you have to always use :
GameObject.GetComponent<T>()
GetComponentInParent<T>()
GetComponentInChildren<T>()
best practice is to get object references at Start()
also you should attach IMPORTANT!!! Animation component to the object this script it attached to
public class DoorPhysics : MonoBehaviour {
public Animation animation;
int Open = 0;
void Start()
{
animation=GameObject.GetComponent<Animation>(); //if your have derived type change Animation to good class DoorAnimation for example
}
void Update()
{
if (Open == 0) {
if (Input.GetKeyDown("e")) {
this.animation.Play("DoorOpen");
}
}
}
}
if that code wont work, you will need show me your GameObject hierarchy
And if your just start your trip with it learn MonoBehaviour call order
and life cycles of events
Related
I using Unity 2019.2.14f1 to create a simple 3D game.
In that game, I want to play a sound anytime my Player collides with a gameObject with a specific tag.
The MainCamera has an Audio Listener and I am using Cinemachine Free Look, that is following my avatar, inside the ThridPersonController (I am using the one that comes on Standard Assets - but I have hidden Ethan and added my own character/avatar).
The gameObject with the tag that I want to destroy has an Audio Source:
In order to make the sound playing on the collision, I started by creating an empty gameObject to serve as the AudioManager, and added a new component (C# script) to it:
using UnityEngine.Audio;
using System;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
// Start is called before the first frame update
void Awake()
{
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
}
}
// Update is called once per frame
public void Play (string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
}
And created the script Sound.cs:
using UnityEngine.Audio;
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume;
[Range(.1f, 3f)]
public float pitch;
[HideInInspector]
public AudioSource source;
}
After that, in the Unity UI, I went to the Inspector in the gameObject AudioManager, and added a new element in the script that I named: CatchingPresent.
On the Third Person Character script, in order to destroy a gameObject (with a specific tag) when colliding with it, I have added the following:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
}
}
It is working properly as that specific object is disappearing on collision. Now, in order to play the sound "CatchingPresent" anytime the Player collides with the object with the tag, in this case, Present, I have tried adding the following to the if in the OnCollisionEnter:
FindObjectOfType<AudioManager>().Play("CatchingPresent");
But I get the error:
The type or namespace name 'AudioManager' could not be found (are you
missing a using directive or an assembly reference?)
AudioManager.instance.Play("CatchingPresent");
But I get the error:
The name 'AudioManager' does not exist in the current context
As all the compiler errors need to be fixed before entering the Playmode, any guidance on how to make the sound playing after a collision between the player and the gameObject with the tag Present is appreciated.
Edit 1: Assuming that it is helpful, here it goes the full ThirdPersonUserControl.cs:
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityStandardAssets.CrossPlatformInput;
namespace UnityStandardAssets.Characters.ThirdPerson
{
[RequireComponent(typeof (ThirdPersonCharacter))]
public class ThirdPersonUserControl : MonoBehaviour
{
public Text countText;
public Text winText;
private int count;
private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
private Transform m_Cam; // A reference to the main camera in the scenes transform
private Vector3 m_CamForward; // The current forward direction of the camera
private Vector3 m_Move;
private bool m_Jump; // the world-relative desired move direction, calculated from the camForward and user input.
private void Start()
{
count = 20;
SetCountText();
winText.text = "";
// get the transform of the main camera
if (Camera.main != null)
{
m_Cam = Camera.main.transform;
}
else
{
Debug.LogWarning(
"Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
// we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
}
// get the third person character ( this should never be null due to require component )
m_Character = GetComponent<ThirdPersonCharacter>();
}
private void Update()
{
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
// Fixed update is called in sync with physics
private void FixedUpdate()
{
// read inputs
float h = CrossPlatformInputManager.GetAxis("Horizontal");
float v = CrossPlatformInputManager.GetAxis("Vertical");
bool crouch = Input.GetKey(KeyCode.C);
// calculate move direction to pass to character
if (m_Cam != null)
{
// calculate camera relative direction to move:
m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
m_Move = v*m_CamForward + h*m_Cam.right;
}
else
{
// we use world-relative directions in the case of no main camera
m_Move = v*Vector3.forward + h*Vector3.right;
}
#if !MOBILE_INPUT
// walk speed multiplier
if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
#endif
// pass all parameters to the character control script
m_Character.Move(m_Move, crouch, m_Jump);
m_Jump = false;
}
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
//FindObjectOfType<AudioManager>().Play("CatchingPresent");
AudioManager.instance.Play("CatchingPresent");
}
}
void SetCountText()
{
countText.text = "Missing: " + count.ToString();
if (count == 0)
{
winText.text = "You saved Christmas!";
}
}
}
}
Edit 2: Hierarchy in Unity:
Reformulated the approach that I was following and solved the problem by simply adding an Audio Source to the ThirdPersonController (with the AudioClip that I wanted to call) and added GetComponent<AudioSource>().Play(); to the if statement as it follows:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.CompareTag("Present"))
{
Destroy(other.gameObject);
count = count - 1;
SetCountText();
GetComponent<AudioSource>().Play();
}
}
Importing your scripts myself works without any issues when using FindObjectOfType<AudioManager>().Play("CatchingPresent");. Try reimporting your scripts from the editor (right click in the project folder > reimport all. this might take a while depending on the size of your project)
to use AudioManager.instance.Play("CatchingPresent"); you would first need to create a static variable that holds instance like this (this only works as a singleton, and will break if multiple AudioManager's are in the scene):
public class AudioManager : MonoBehaviour
{
//Create a static AudioManager that will hold the reference to this instance of AudioManager
public static AudioManager Instance;
public Sound[] sounds;
//Assign Instance to the instance of this AudioManager in the constructor
AudioManager()
{
Instance = this;
}
// Rest of the AudioManager code
}
Doing it like this, and using the rest of your code also works for me.
Im making a simple game in Unity 3d, basically you have a backdround object with more gameobjects on top, what you have to do is drag those game objects into some trash cans, as soon as you hit the correct trash can with the correct object said object gets deleted, the idea of the game is that when you put all the objects in the corect cans you win the game
I have the drag and acceptance all made but i cant figure out how to make the script so that when theres no more objects, you win the game.
I've been trying to use Hierarchy on an empty game object thats the parent for the objects that you have to destroy, but i dont know how to actually delete the objects so that the code can register it, any help?
The code for the drag and the code im using for the trash cans:
using UnityEngine;
using UnityEngine.EventSystems;
public class arrastrar : MonoBehaviour, IDragHandler
{
public float z = 0.0f;
public void OnDrag(PointerEventData eventData)
{
Vector3 mousePosition = Input.mousePosition;
mousePosition.z = z;
transform.position = Camera.main.ScreenToWorldPoint(mousePosition);
}
}
And the code inside the trashcans
using System.Collections;
using UnityEngine;
using System.Collections.Generic;
public class botarBasura: MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.name == "cubo")
Destroy(other.gameObject);
} else if (other.gameObject.name == "escombros")
{
other.gameObject.transform.position = new Vector3(-1, 2.5f, -2); //If its not the correct trash can it returns the object to the starting position
}
} else if ( other.gameObject.name == "botellas")
{
other.gameObject.transform.position = new Vector3(0, 2.5f, -2);
}
}
}
You can use a scriptable object to keep track of your object. Objects just need to add and remove themselves to a list in the scriptable object in their OnEnable() and OnDisable() callbacks. When an object removes itself you can have code check to see if there are no more objects left and trigger an event that something in your scene will react to.
(Dragable Object)
public class DragableObject : MonoBehaviour{
//reference to the scriptable object
public DragableObjectSet RuntimeSet;
void OnEnable(){
RuntimeSet.Add(this);
}
void OnDisable(){
RuntimeSet.Remove(this);
//you can put code checking if the runtime set is empty here
//or you can put it in the DragableObjectSet Remove method
}
}
Scriptable object example code
public DragableObjectSet : ScriptableObject
{
public List<DragableObject> RuntimeSet = new List<DragableObject>();
public void Add(DragableObject obj){
RuntimeSet.Add(obj);
}
public void Remove(DragableObject obj){
RuntimeSet.Remove(obj);
if(RuntimeSet.Count() == 0)
//some code here raising an event (if desired)
}
}
This type of object is called a runtime set and is detailed in this talk around the 40 minute mark.
https://www.youtube.com/watch?v=raQ3iHhE_Kk
A solution could possibly be using a singleton:
Create a new empty GameObject and add this code:
using UnityEngine;
public class TrashCounter : MonoBehaviour {
public static TrashCounter instance = null;
public int counter = 0;
void Awake(){ instance = this; }
}
Now, wherever you create an object (or in the Start() method of any object code), you just need to do TrashCounter.instance.counter++ and you will have your trash count.
After that, you just need to update this counter everytime you delete an object by doing TrashCounter.instance.counter-- and after you ask if(TrashCounter.instance.counter == 0) WinGame().
You can get an array of all active & not-yet-destroyed GameObjects with some enabled component with Object.FindObjectsOfType<ComponentType>();. Once you have that, you could then look at the length of that array.
So, you could do something like this in the code that destroys/deactivates the draggable gameobject:
GameObject otherObject = /* get draggable object to possibly destroy */;
if ( /* test to remove otherObject */ )
{
// deactivate the draggable object
otherObject.setActive = false;
// get all other draggable objects
DraggableObject[] remainingDraggableObjects = Object.FindObjectsOfType<DraggableObject>();
if (remainingDraggableObjects.Length == 0)
{
// there are no more active gameobjects with enabled DraggableObject component
// handle win condition here.
}
}
In your specific example, it could look like this:
void OnCollisionEnter(Collision other)
{
if (other.gameObject.name == "cubo")
{
other.gameObject.setActive = false; // better than destroying in most cases
// get all other draggable objects
arrastrar[] remainingObjects = Object.FindObjectsOfType<arrastrar>();
if (remainingObjects.Length == 0)
{
// there are no more active gameobjects with enabled arrastrar component
// handle win condition here.
}
}
// ...
I have created a game where when the user breaks all the blocks he is taken to the next scene but this is not happening despite adding all of the scenes I have in the build settings. I have no errors whatsoever and the scene is written correctly. Can someone help me resolve this, please?
This is the build settings
Bricks script : (where the scene is called)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public static int brickCount = 0;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
if(this.gameObject.tag == "BrickHit")
{
brickCount++;
}
if(this.gameObject.tag == "BrickHitTwice")
{
brickCount++;
}
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
brickCount--;
Destroy(this.gameObject);
}
if(brickCount == 0)
{
myLevelManager.LoadLevel("Level1.2"); //THIS SCENE IS NOT LOADING
}
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
LevelManager Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
I suspect that your bug may lie in the fact that you're Destroy()ing the gameObject before it can load the next scene; you get a race condition on what will finish first; LoadScene or Destroy() - which would explain why it sometimes work. You should never assume it is a bug in the framework before understanding your problem.
Try putting the Destroy() after the LoadScene() or with a delay to understand if this is your issue.
Also, your LevelManager can be made static and doesn't need to inherit from MonoBehaviour since it doesn't use gameObject functionality.
public static class LevelManager {
public static void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Used by doing LevelManager.LoadLevel("MyLevel");, but then you may question what is more effective, doing LevelManager.LoadLevel or SceneManager.LoadLevel, as they will do the exact same thing.
The main issue that you're having is not having a single source to check brickCount instead each individual brick is maintaining its own count. I would recommend moving the brick counting logic into a separate class. It would seem like LevelManager would a good place for it. So in LevelManager add:
private int brickCount = 0;
public void AddBrick()
{
brickCount++;
}
public void RemoveBrick()
{
brickCount--;
// Check if all bricks are destroyed
if (brickCount == 0)
{
LoadLevel("Level1.2");
}
}
And then in your brick script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
myLevelManager.AddBrick();
// I'm not sure why you were checking the tag here, since the result was the same
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
myLevelManager.RemoveBrick();
Destroy(this.gameObject);
}
/* This looks like the player is getting score whether the brick is destroyed or not. Also, it would appear the player won't get scored on the final brick */
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
}
So, I'm struggling with the whole Unet system. right now, all I want to do is to spawn object from one end to the other. but even that doesn't work. It spawns the object on each the server and the client. but doesn't sync. I've searched so many places and watched so many videos - none helped. here are the code and the inspector details
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class NetworkController : NetworkBehaviour
{
public GameObject[] Spawns;
GameObject Canvas;
GameObject spawn;
[SyncVar]
public NetworkInstanceId ParentId;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
[Command]
public void CmdspawnPrefab()
{
Transform trns = GameObject.Find("Canvas").transform;
trns.position = trns.position + new Vector3(100, 200, 0);
GameObject go = Instantiate(Spawns[0], trns);
NetworkServer.Spawn(go);
}
}
What am I missing?
As it seems, my whole workflow was not correct. the solution is firstly to attach a listener to the button, instead of using the onclick function on the inspector -
Button b = BoardController.Buttons[0].GetComponent<Button>();
b.onClick.AddListener(delegate () { Cmd_ButtonPressed(1); });
the second is to create gameobjects, containing a script "GameManager" who controls your board, and one "RpcManager" which contains the Rpc functions. then in the playerobject script, use the commands.
Here are the classes i have used, to update an image instead of spawning an object, the idea is basically the same - to undersand the fundamentals of Unet and passing commands.
public class PlayerObject : NetworkBehaviour {
public BoardManager BoardController;
public ClientRPCmanager ClientRPCManager;
// Use this for initialization
void Start () {
ClientRPCManager = GameObject.FindGameObjectWithTag("ClientRPCmanager").GetComponent<ClientRPCmanager>();
BoardController = ClientRPCManager.BoardController;
Button b = BoardController.Buttons[0].GetComponent<Button>();
b.onClick.AddListener(delegate () { Cmd_ButtonPressed(1); });
}
// Update is called once per frame
void Update () {
}
public void SetButton(int i)
{
BoardController.SetButton(i);
}
[Command]
public void Cmd_ButtonPressed(int i)
{
ClientRPCManager.Rpc_ButtonPressed(i);
}
boardmanager
public class BoardManager : NetworkBehaviour
{
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public GameObject[] Buttons;
public Sprite[] Sprites;
public void SetButton(int index)
{
GameObject img = GameObject.Find("X");
img.GetComponent<Image>().sprite = Sprites[0];
}
RpcManager
public class ClientRPCmanager : NetworkBehaviour {
public BoardManager BoardController;
public NetworkManager Manager;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
[ClientRpc]
public void Rpc_ButtonPressed(int index)
{
BoardController.SetButton(index);
}
hopefully, this will somewhat help people with understanding Unet.
I learned this by carefully looking at this project https://www.youtube.com/watch?v=8Kd2RAfgzW0
I need to make an image pop up when the player has died or crashed but i do not know how to do it, i'm trying to make a game in unity using c#
but i have made a code that will tell show the user an image before they start (tap to start image) and all i want to do is display another one that tell to user to start again
does the code have to be similar to this or do i have to start from scratch?
public class StartScreenScript : MonoBehaviour {
static bool sawOnce = false;
// Use this for initialization
void Start () {
if(!sawOnce) {
GetComponent<SpriteRenderer>().enabled = true;
Time.timeScale = 0;
}
sawOnce = true;
}
// Update is called once per frame
void Update () {
if(Time.timeScale==0 && (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0)) ) {
Time.timeScale = 1;
GetComponent<SpriteRenderer>().enabled = false;
}
}
}
this code show the an image telling the user to tap the screen and the image then goes away until the user closes the game then comes back on however i want to display a "you are dead image" every time the player dies can someone please help me
p.s this is for a 2d game
Well one way to do it would be using Unity3D's GUI.DrawTexture that given a texture draws it at a given position. Here is a sample call to the method.
GUI.DrawTexture(new Rect(leftAnchor, topAnchor, textureWidth, textureHeight), textureSource);
This is my approach and it works in most cases:
Create a GameObject that will work as a dead screen. Apply a sprite
or whatever telling the user to click to restart.
Add the previous GameObject to the PlayerController so he can
instantiate it.
When the player is dead call PlayerController.ShowDeadScreen()
When the user clicks inside DeadScreen GameObject it will call your
PlayerController.PlayAgain function and destroy itself. So you must handle everything
the game need to be restarted.
PlayerController example code
public class PlayerController : MonoBehaviour {
public GameObject deadScreen;
void Start() { }
void Update() { }
public void ShowDeadScreen()
{
// show DeadScreen GameObject on the center of the screen
GameObject go = Instantiate(deadScreen, new Vector(0, 0, 0), Quaternation.Identity) as GameObject;
go.playerController = this;
}
public void PlayAgain()
{
// handle game restart
}
}
DeadScreen example code
public class DeadScreen : MonoBehaviour {
public PlayerController playerController;
void Start() { }
void Update() { }
void OnMouseDown()
{
// when user clicks inside this GameObject start the game again
playerController.PlayAgain();
Destroy(this.gameObject);
}
}