I'm working on a Unity2D platformer which has blocks that break/fall when the player jumps onto them. Once the player makes contact, there is a short delay, then the block begins to slide down and shortly disappears (using SetActive(false)). If the player dies, then all "falling" blocks should respawn and return to their original positions.
I've made a level where there are two of these blocks (two completely independent gameobjects), but the problem is that when the player breaks both and then dies, when the player respawns only one of the two blocks respawns to its original position. (The other is never to be seen again.)
I think this may have something to do with how they are both attached to the same script. However I don't see how because a different gameobject is still specified in each instance of the script for each block.
Here's the code for the script attached to the block gameobjects (called breakingBlock).
public class BreakingBlock : MonoBehaviour
{
Collider2D boxCollider;
public GameObject slidingBlock;
public PlayerController player;
public float delay;
public float X;
public float startY;
public float endY;
public float speed;
void Start() // Initialises the position of the blocks
{
boxCollider = GetComponent<Collider2D>();
boxCollider.enabled = true;
slidingBlock.transform.position = new Vector2(X, startY);
player = FindObjectOfType<PlayerController>();
}
void OnCollisionEnter2D(Collision2D collision)
// Starts sliding coroutine when touched by player
{
StartCoroutine(Slide(slidingBlock, new Vector2(X, startY), new Vector2(X, endY), delay, speed));
}
public IEnumerator Slide(GameObject slidingBlock, Vector2 start, Vector2 end, float delay, float speed)
{
yield return new WaitForSeconds(delay);
while (slidingBlock.transform.position.y != end.y)
{
// Move towards end position over duration given
slidingBlock.transform.position = Vector2.MoveTowards(slidingBlock.transform.position, end, speed * Time.deltaTime);
yield return new WaitForEndOfFrame();
}
boxCollider.enabled = false;
slidingBlock.SetActive(false);
}
public void ResetSlidingBlock() // Called by another script when player dies
{
Debug.Log("Reset block");
slidingBlock.transform.position = new Vector2(X, startY);
slidingBlock.SetActive(true);
boxCollider.enabled = true;
}
}
This is the part of the other script that calls ResetSlidingBlock from the above script breakingBlock when the player dies.
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player")
{
levelManager.RespawnPlayer();
breakingBlock.ResetSlidingBlock();
}
}
Why is it that only one of the blocks is respawning when separate gameobjects are specified in the separate scripts on completely distinct gameobjects?
You have a public GameObject where only one object is assigned. Doesn't matter how many objects you assign this script to, only that GameObject that you refer to will be reset. If you have X number of GameObjects that you want to follow the same properties, you can:
Make a prefab and access the children using transform.getchild() or tags - assign them to GameObjects
ex:
GameObject objs[];
for(int i=0;i<How many u want;i++)
{
objs[i]=GameObject.Find("ParentObj").transform.GetChild(i);
//something similar to this
}
Or Simply Make public references and assign them manually.
Ex:
public GameObject slideblock1;
public GameObject slideblock2;
etc.
Related
There is a problem with my bullet firing logic.
The first time everything works fine, but when I shoot the bullet after respawning it doesn't move. It only works for the first shot.
The bullet is a prefab.
Here is my code:
public Rigidbody2D bulletrb;
private float dirX=1;
public float speed=.001f;
public Transform playerPos;
private Vector3 bulletPos;
public GameObject bulletObj;
void Update () {
bulletPos.x = playerPos.transform.position.x + 2;
bulletPos.y = playerPos.transform.position.y + 1;
if (Input.GetKeyDown("1"))
{
bulletrb.velocity = new Vector2(dirX * speed, bulletrb.velocity.y);
Debug.Log("Shoot!");
}
}
private void OnTriggerEnter2D(Collider2D target)
{
if(target.gameObject.tag=="Zombie")
{
Destroy(bulletObj);
Debug.Log("Hited!");
Instantiate(bulletObj,bulletPos,Quaternion.identity,playerPos);
}
}
When you call Destroy, you are losing your active bullet GameObject, but when you call Instantiate, it is not actually assigned back to your bulletObj reference.
The quick and simple fix is:
bulletObj = Instantiate(bulletObj, bulletPos, Quaternion.identity, playerPos);
There are probably more improvements that could be made, but this is the gist of the issue.
One recommendation I would make is using bulletObj.SetActive(false) and bulletObj.SetActive(true) instead of Destroy and Instantiate
I'm using Unity and C# and I am fairly new to both.
I have a class called Pathfinding attached to a empty gameobject. It requires a link to a start gameobject and an end gameobject and makes a list of nodes between the two in the shortest path. (A*).
What I need help with is:
How do I instantiate my enemy gameobject from a prefab and link it to this script (usually done by dragging the gameobject onto the respective tile in the editor).
How can I make a gameobject move by following the list created by the pathfinding algorithm (maybe takes the first one of the list, moves, then recalculates)? More info: My board is divided into many small cubes, the algorithm uses these to create a path. The path (list of these nodes) is stored. I want multiple enemies to
Thank you very much. Ask me if its not clear enough and I'll try to provide screenshots or info. :)
1.
Instantiate enemy and add it to a list (which is in your script):
using UnityEngine;
public class Controller: MonoBehaviour
{
GameObject enemyPrefab;
List<GameObject> enemiesList = new List<GameObject>();
void Start()
{
GameObject enemy = Instantiate(enemiePrefab, transform.position, transform.rotation);
enemiesList.Add(enemy);
}
}
2.
Make the object move on your pathfinding:
There are many ways to do that, but I suggest you to store in a List, array or whatever you want, the different waypoints that you have.
Then make your enemies follow one waypoint after another.
So now your main script should look like:
using UnityEngine;
public class Controller : MonoBehaviour
{
public GameObject enemyPrefab;
private List<GameObject> enemiesList = new List<GameObject>();
public List<GameObject> wayPoints = new List<GameObject>();
void Start()
{
GameObject enemy = Instantiate(enemiePrefab, transform.position, transform.rotation);
enemy.wayPoints = wayPoints;
enemiesList.Add(enemy);
}
}
And your enemy script should look like:
using UnityEngine;
public class Enemy : MonoBehaviour
{
public List<GameObject> wayPoints = new List<GameObject>();
public float speed;
private Transform target;
int waypointIndex = 0;
private void Start()
{
target = List[waypointIndex].transform;
waypointIndex++;
}
void Update()
{
// The step size is equal to speed times frame time.
float step = speed * Time.deltaTime;
// Move our position a step closer to the target.
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
//If we arrive to the target, get the new target
if(this.transform.position == target.position)
{
waypointIndex++;
//if it is the last element, go to the first one again
if(wayPointIndex > List.Count())
{
wayPointIndex = 0;
}
target = List[waypointIndex].transform;
}
}
}
Note that you can alterate the code so your controller also moves every enemy, but that's up to you!
Already 2 days I'm trying to solve this problem but I can't.I'm trying to delete the unlimited spawning barriers consistently when they are collide with the boundary.The barriers are made from 5 cubes.Any ideas?
public List<GameObject> spawning=new List<GameObject>();
public Vector3[] positions = new Vector3[5];
public GameObject barrier;
public GameObject boundary;
void Start()
{
StartCoroutine (SpawnBarrier());
}
void Update()
{
if(true)
{
foreach (GameObject move in spawning)
move.transform.Translate (0f, 0f, -0.1f);
}
}
IEnumerator SpawnBarrier(){
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
yield return new WaitForSeconds (3f);
}
}
Your barriers are marked as isTrigger on their colliders therefore you can use OnTriggerEnter to detect when any of the barriers collides with the boundary.
You need to create new script, lets call that BoundaryDetector and attach it to the barrier prefab so that every instance of the barrier will have this script attached to it.
When OnTriggerEnter is called, check if the trigger is made by the boundary. This can be done by checking for the "Player" tag since boundary is tagged as Player in your screenshot. If the detected trigger tag is "Player", first remove the spawning from the spawning List then Destroy it.
The BoundaryDetector script is as below (Must be attached to the barrier prefab):
ScriptFromYourQuestion yourInstancingSript;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectScriptInYourQuestionIsAttachedTo");
yourInstancingSript = obj.GetComponent<ScriptFromYourQuestion>();
}
void OnTriggerEnter(Collider other)
{
//Detect if we collided with the boundary
if (other.CompareTag("Player"))
{
//Remove Self/barrier from the List
yourInstancingSript.spawning.Remove(this.gameObject);
//Delete Self/barrier
Destroy(this.gameObject);
}
}
Note: The ScriptFromYourQuestion should be replaced with the name of the script in your question.
MUST DO:
To get the code above working, the following changes must be made in the Update function of the script from your question:
1.You must attach Rigidbody to at-least one of the Objects (boundary or barrier). In this case, I suggest you do so to the barrier prefab.
2.You must remove move.transform.Translate (0f, 0f, -0.1f); and replace it with Rigidbody.MovePosition and use that to move your barries because you have now attached Rigidbody to your barries and this is the proper way to move a Rigidbody Object.
Maybe something like this:
Your Update function in the code form your quesion should looks something like this:
public float speed = 100.0f;
void Update()
{
if (true)
{
foreach (GameObject move in spawning)
{
//Get Rigidbody component
Rigidbody rb = move.GetComponent<Rigidbody>();
//Calculate Z-axis pos to move to
Vector3 pos = new Vector3(0, 0, 1);
pos = pos.normalized * speed * Time.deltaTime;
//Move with Rigidbody
rb.MovePosition(rb.transform.position + pos);
}
}
}
Your problem is that you have an infinite loop while true that you're not breaking out of. Instead you should add a counter. You haven't explained what you want to do but my example is that it will breakout once it completes 10 instantiations of 5 blocks.
IEnumerator SpawnBarrier(){
spawnCount=0
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
if (++spawnCount==10)
{
break;
}
yield return new WaitForSeconds (3f);
}
}
If I understood corrently, you want to destroy barriers that collided with some kind of boundary?
If the 'boundary' is the visible screen space
In this case, we can assume that, once the object is invisible to MainCamera it can be counted as out of the boundary. Keeping that in mind, we can use OnBecameInvisible() method, which is method (or message, as stated in Unity Scripting API) called by MonoBehaviour class:
// A piece of barrier that gets destroyed once out of screen
public sealed class BarrierPiece : MonoBehaviour
{
// Called once the object is no longer visible to any (scene editor included) camera
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
NOTE: if you have more than one Camera in your scene, the object must be invisible to all of the cameras in order for the OnBecameInvisible() to be called.
If the 'boundary' is other object
In this case, there are many approaches based on your game:
Create GameObject with Rigidbody component and add a script which would have OnTriggerEnter(...) method and destroy colliding barriers in it.
(Inefficient) have two static Vector2 variables, one for pivotPosition, other for maximumOffset (this one should be squared for faster calculations). Then, have script on each of the barriers and in Update() checks whether the object's squared distance to the pivotPosition is greater than maximumOffset and call Destroy(gameObject) if the condition is true.
(others) ...
Those are two that came up quickly in my mind.
Hope this helps!
EDIT
Didn't look at the images...
So for your solution, you would need to assign Rigidbody component to the Boundary and add BoxCollider component on all of the barriers. Now, create a tag, called Barrier and tag all the barriers with it. Create a script:
// Destroys barriers on collision
public sealed class BarrierBoundary : MonoBehaviour
{
// Called once the script is created
// Checks if the object has Rigidbody component attached
private void Awake()
{
Debug.Assert(GetComponent<Rigidbody>() != null);
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Barrier")
Destroy(collision.collider.gameObject);
}
}
(The code wasn't tested, so it can have some typos.)
Now, assign the script to the Boundary. And voila! Barriers are destroyed once hitting the boundary.
Hope (again) this helps!
I am very new to Unity and I never coded in C# before neither have I used Unity in the past. All I know is Java and Python. I knew Java since senior year of high school now I am a sophomore in college. I made my project, which consists of my human character, which is supposed to shoot bullets at 12 jaguars. I already implemented all of this from the 2d UFO sample Unity gives us. I have a png picture for my bullet. I really do not know how to implement a bullet. I am required to have the bullet shoot out in two directions. I am starting off gradually by learning how to basic shoot (one direction). I referred to this link and added it to my program and it will not compile after I added the bullet code. I create a sphere into the Unity program as the website said to do and I set the image as the png bullet.
https://unity3d.com/learn/tutorials/temas/multiplayer-networking/shooting-single-player
Now here is my code. I downloaded the free latest version of Unity by the way. My game is 2D by the way. It is just cartoons.
using UnityEngine;
using System.Collections;
//Adding this allows us to access members of the UI namespace including Text.
using UnityEngine.UI;
public class CompletePlayerController : MonoBehaviour {
public float speed; //Floating point variable to store the player's movement speed.
public Text countText; //Store a reference to the UI Text component which will display the number of pickups collected.
public Text winText; //Store a reference to the UI Text component which will display the 'You win' message.
private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics.
private int count; //Integer to store the number of pickups collected so far.
public GameObject bulletPrefab;
public Transform bulletSpawn;
// Use this for initialization
void Start()
{
//Get and store a reference to the Rigidbody2D component so that we can access it.
rb2d = GetComponent<Rigidbody2D> ();
//Initialize count to zero.
count = 0;
//Initialze winText to a blank string since we haven't won yet at beginning.
winText.text = "";
//Call our SetCountText function which will update the text with the current value for count.
SetCountText ();
}
//FixedUpdate is called at a fixed interval and is independent of frame rate. Put physics code here.
void FixedUpdate()
{
//Store the current horizontal input in the float moveHorizontal.
float moveHorizontal = Input.GetAxis ("Horizontal");
//Store the current vertical input in the float moveVertical.
float moveVertical = Input.GetAxis ("Vertical");
//Use the two store floats to create a new Vector2 variable movement.
Vector2 movement = new Vector2 (moveHorizontal, moveVertical);
//Call the AddForce function of our Rigidbody2D rb2d supplying movement multiplied by speed to move our player.
rb2d.AddForce (movement * speed);
if (!isLocalPlayer)
{
return;
}
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
transform.Rotate(0, x, 0);
transform.Translate(0, 0, z);
if (Input.GetKeyDown(KeyCode.Space))
{
Fire();
}
}
void Fire()
{
// Create the Bullet from the Bullet Prefab
var bullet = (GameObject)Instantiate (
bulletPrefab,
bulletSpawn.position,
bulletSpawn.rotation);
// Add velocity to the bullet
bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;
// Destroy the bullet after 2 seconds
Destroy(bullet, 2.0f);
}
public override void OnStartLocalPlayer ()
{
GetComponent<MeshRenderer>().material.color = Color.blue;
}
//OnTriggerEnter2D is called whenever this object overlaps with a trigger collider.
void OnTriggerEnter2D(Collider2D other)
{
//Check the provided Collider2D parameter other to see if it is tagged "PickUp", if it is...
if (other.gameObject.CompareTag ("PickUp"))
{
//... then set the other object we just collided with to inactive.
other.gameObject.SetActive(false);
transform.localScale += new Vector3(0.1f, 0.1f, 0);
//Add one to the current value of our count variable.
count = count + 1;
//Update the currently displayed count by calling the SetCountText function.
SetCountText ();
}
}
//This function updates the text displaying the number of objects we've collected and displays our victory message if we've collected all of them.
void SetCountText()
{
//Set the text property of our our countText object to "Count: " followed by the number stored in our count variable.
countText.text = "Count: " + count.ToString ();
//Check if we've collected all 12 pickups. If we have...
if (count >= 12)
//... then set the text property of our winText object to "You win!"
winText.text = "You win!";
}
}
Other class
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour {
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start ()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate ()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
You need to follow all the steps in the tutorial link you shared. Looks like you have copied pieces of code and used it in your controller class. OnStartLocalPlayer is marked as overridden, but it is not in Monobehaviour. It is defined in NetworkBehaviour. You have an error due to this. As mentioned in the other comments, the console error tells you where the problem is.
This code is for an elevator-type platform, where once the player stands on it, it 'takes' the player up by adding force onto it.
The thing is, while the force is created, the rigidbody (the player) does not move when the elevator moves. The code was written in C#, using Unity 5. In the code, the player is assigned the public 'rb', and contains a rigidbody.
The animation is a simple animation clip that moves the elevator up. Any ideas? Thank you for your time and answers in advance.
The elevator is Kinematic, the Player is not.
using UnityEngine;
using System.Collections;
/*This script activates when the player steps on the elevator, as it takes them up a floor.*/
public class ElevatorMovementScript : MonoBehaviour
{
private bool elevatorUp = false;
public Animation anim;
public int elevatorDelay = 5;
public int force = 800;
public Rigidbody rb;
// Use this for initialization
void Start ()
{
anim = GetComponent<Animation>();
}
// Update is called once per frame
void Update ()
{
}
/*Checks if the player has stepped onto the elevator. If the player has, it waits five seconds, and then pushes the player up.*/
void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player" && !elevatorUp)
{
Invoke("AnimationPlay",elevatorDelay);
elevatorUp = true;
}
}
/*Plays the animation of the player going up. Used for the 'Invoke' method.*/
void AnimationPlay()
{
rb.AddForce(transform.up * force);
Debug.Log (transform.up * force);
anim.Play ("Up");
}
}
It looks like this script is on your elevator's game object, in which case this line:
rb.AddForce(transform.up * force);
Will try to apply a force to the elevator, not the player. You've got to keep track of the player's rigidbody or somehow get it on demand in AnimationPlay.
You said that
the player is assigned the public 'rb'
But rb = GetComponent<Rigidbody>(); will ignore this and use the rigidbody attached to the gameobject that ElevatorMovementScript is attached to.