Alright, so I've been building a small thing in Unity 2D, and it works for the most part, however whenever I attempt to create a script to spawn a copy of the circle prefab, it just doesn't spawn without any error messages.
I've attempted to use Instantiate to spawn them at the button's location, yet no luck.
{
public GameObject circle;
public Transform circlespawn;
private bool touched = false;
void Update()
{
if(touched == true)
{
Instantiate(circle, circlespawn.position, circlespawn.rotation);
touched = false;
}
}
void OnMouseDown()
{
touched = true;
}
}
From BugFinder in the comments, he pointed out I simply needed a collider for the onmousedown to work correctly.
Related
In the game I'm making, I clone a Ball object whenever a "split ball" power-up is acquired. Everything works as intended except the TrailRenderer material. The Ball prefab has a default material used for TrailRenderer in the beginning. This, however, changes when the ball hits an object controlled by the player (which is called a "bumper"). The material changes work perfectly on collision. Here is the shortened script for the Ball object:
[NonSerialized]
public TrailRenderer trailRenderer;
[SerializeField]
private Material defaultTrailMaterial;
void Start()
{
trailRenderer = GetComponent<TrailRenderer>();
trailRenderer.material = defaultTrailMaterial;
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (CollidedObjectIsABumper(collision))
{
// SetTrailColorToBumperColor() is called along other calculations
}
}
private bool CollidedObjectIsABumper(Collision2D collision)
{
return collision.gameObject.CompareTag("Bumper");
}
private void SetTrailColorToBumperColor()
{
trailRenderer.material = lastCollidedBumper.material;
}
I discarded a few things for clarity.
Now, here is the shortened script for the split ball power-up:
void OnTriggerEnter2D(Collider2D collision)
{
if (!CollidedWithABall(collision))
return;
Ball mainBall = collision.gameObject.GetComponent<Ball>();
// I added this part as a desperate attempt, didn't work.
mainBall.trailRenderer.material = mainBall.lastCollidedBumper.material;
Ball splitBall = Instantiate(mainBall, pos, Quaternion.identity);
splitBall.tag = "Split Ball";
splitBall.GetComponent<TrailRenderer>().material = mainBall.lastCollidedBumper.material;
Destroy(gameObject);
}
private bool CollidedWithABall(Collider2D collision)
{
return collision.gameObject.CompareTag("Ball") || collision.gameObject.CompareTag("Split Ball");
}
pos is a Vector3 variable that is declared in the cut portion. After getting the power-up, this is how the game scene looks like:
None of the balls touched a bumper after getting the power-up. I expect the split ball to have a red trail but it doesn't. I'm sure I'm missing something with Instantiate() but I don't know what.
One thing I assumed was that Instantiate() used the prefab of the main ball, in which case the trail would have a neutral color, but I added an assignment statement after Instantiate() so I don't think that's the only problem here. For reference, the split ball DOES change its trail color when it hits a bumper.
Thank you and please let me know if you need additional information.
As mentioned this is a timing issue.
You have
void Start()
{
trailRenderer = GetComponent<TrailRenderer>();
trailRenderer.material = defaultTrailMaterial;
}
which overwrites your material.
The call of Start on new instantiated objects is delayed until the beginning the next frame for the purpose of being able to still change some field values right after Instantiate before Start is called.
So you set the material in
splitBall.GetComponent<TrailRenderer>().material = mainBall.lastCollidedBumper.material;
But then it is later changed again by Start.
Awake however is called right away.
So either you change it to
void Awake()
{
trailRenderer = GetComponent<TrailRenderer>();
trailRenderer.material = defaultTrailMaterial;
}
so this is done first and then the line
splitBall.GetComponent<TrailRenderer>().material = mainBall.lastCollidedBumper.material;
can correctly overwrite the material or alternatively you could also make
public Material defaultTrailMaterial;
and instead of directly setting the material in
splitBall.GetComponent<TrailRenderer>().material = mainBall.lastCollidedBumper.material;
you rather only set
splitBall.GetComponent<YourComponent>().defaultTrailMaterial = mainBall.lastCollidedBumper.material;
and then let Start do the job as currently.
Anyone can help me how to fix this problem i'm having?
*1st script:
public static bool attacking;
public Collider2D attackTrigger;
private void Awake()
{
attackTrigger.enabled = false;
}
private void Update()
{
if (attacking == true)
{
Debug.Log("Box Collider Enabled");
attackTrigger.enabled = true;
StartCoroutine(DisableCollider());
}
}
IEnumerator DisableCollider()
{
yield return new WaitForSeconds(1);
attacking = false;
attackTrigger.enabled = false;
Debug.Log("Box Collider Disabled");
}
*2nd script:
public float damage = .10f;
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.isTrigger!=true && collision.CompareTag("Enemy"))
{
Debug.Log("Enemy is Damaged");
EHealthBar.TakeDamage(damage);
}
}
i'm trying to get my player character to attack an enemy using collision, well it does work and the player does damage the enemy but it will only work if the enemy's box collider enters my attack again.
if the enemy is already in my area of attack which has a box collision 2d attached, the enemy doesn't get damaged and that's not what i was going for.
I can change it to OnTriggerStay2D but the enemy will keep on getting damaged till the collider is disabled again. care to help?
I think the problem is
IEnumerator DisableCollider()
{
yield return new WaitForSeconds(1);
attacking = false;
...
which causes that your IF statement in your update is still occuring for 1 second and starting a lot of coroutines. The easiest solution would be moving attacking = false; before yield, but i guess you want to use that variable to be aware if user is in attack state. So i suggest to use another bool variable.
if (invokeAttack)
{
attacking = true;
invokeAttack = false
Debug.Log("Box Collider Enabled");
attackTrigger.enabled = true;
StartCoroutine(DisableCollider());
}
Anyway, you didn't show us how you invoke attack, but maybe instead of changing variable (attacking/invokeAttack in your case) to true and checking it in Update you just invoke a method which enables collider and starts coroutine?
I am working on a tutorial and the "Side objectives" that they don't walk you through to try and get a feel for it.
So, the way things work at this time is that there is the Player object. The player object has the player script.
public class Player : MonoBehaviour {
private Animator anim;//reference for animator component
private Rigidbody rigidBody;//reference to component for rigidbody
private AudioSource audioSource;
[SerializeField] private float force = 100f;
[SerializeField] private AudioClip sfxJump;
[SerializeField] private AudioClip sfxDeath;
}
void Awake() {//these are assertions that will ensure when writing the cocde that you wont miss them. use for team work.
Assert.IsNotNull (sfxJump);
Assert.IsNotNull (sfxDeath);
}
private bool jump = false; //check for jump
// Use this for initialization
void Start () {//all these are getting components at the start to update them as the code goes onwards.
anim = GetComponent<Animator> ();
rigidBody = GetComponent<Rigidbody> ();
audioSource = GetComponent<AudioSource> ();
positionStart = GetComponent<Transform> ();
}
// Update is called once per frame
void Update () {
if (!GameManager.instance.GameOver && GameManager.instance.GameStarted) {
if (Input.GetMouseButtonDown (0)) {//if press mouse key
GameManager.instance.PlayerStarted ();
rigidBody.useGravity = true;//turn gravity on for component so it goes back down.
audioSource.PlayOneShot (sfxJump);
anim.Play ("jump");//play the animation jump
jump = true;
}
}
}
//Fixed update for physics
void FixedUpdate() {//use this for any physics due to frame rate. time.deltatime wont cut it.
if (jump == true) {//if we are jumping, turn the jump off.
jump = false;
rigidBody.velocity = new Vector2 (0, 0);//turn velocity to 0 so speed doesnt increase while falling
rigidBody.AddForce (new Vector2 (0, force), ForceMode.Impulse);//give a impulse upwards.
}
//print (rigidBody.velocity.y);//print velocity. turn this shit off.
}
//Code to create collision with obstacles and then die and fall through the floor.
void OnCollisionEnter (Collision collision) {//call collision component
if (collision.gameObject.tag == "obstacle") {//if you slap a tagged object called obstacle
rigidBody.AddForce (new Vector2 (-50, 20), ForceMode.Impulse);//add force to push back cause you ded
rigidBody.detectCollisions = false;//turn off the ability to detect collisions
audioSource.PlayOneShot (sfxDeath);//play ded noise
GameManager.instance.PlayerCollided ();
GameManager.instance.Restart ();
}
}
}
The game manager, of course exists in the camera to control the states of the game.
public static GameManager instance = null;//only one in memory. only one gamemanager ever.
[SerializeField] private GameObject mainMenu;
[SerializeField] private GameObject replayBtn;
[SerializeField] private GameObject playBtn;
private bool gameEnd = false;
private bool gameStarted = false;
private bool playerActive = false;
private bool gameOver = false;
//getters setters start
public bool PlayerActive {
get { return playerActive; }
}
public bool GameOver {
get { return gameOver; }
}
public bool GameStarted {
get { return gameStarted; }
}
//to create a state between gameover and main menu
public bool GameEnd {
get {return gameEnd; }
}
//getter setters end
void Awake(){
if (instance == null) {
instance = this;//this means the current instance. one instance of this class.
} else if (instance != this) {//if a seocnd one gets created destroy that bitch.
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject);//allows a game object to persist between the scene. Dont need with one scene.
}
// Use this for initialization
void Start () {
replayBtn.SetActive (false);
}
// Update is called once per frame
void Update () {
}
public void PlayerCollided(){
gameOver = true;
}
public void PlayerStarted(){
playerActive = true;
}
public void EnterGame (){
mainMenu.SetActive(false);
gameStarted = true;
}
//When player dies start coroutine Hold.
public void Restart (){
StartCoroutine (Holdexit());
}
//The hole Coroutine waits 2 seconds then turns on the menu.
IEnumerator Holdexit (){
yield return new WaitForSeconds (2);
playBtn.SetActive (false);
replayBtn.SetActive (true);
mainMenu.SetActive (true);
//add character movement to location
}
}
So, When the player hits the object he dies, loses the ability to touch colliders and falls through the map, after 2 seconds the main menu comes back and the play button is replaced with a replay button. When I press replay, I need to reset the position, the state of the game, and the ability to collide.
I Tried all kinds of things. I did a get component for transform and tried to call it in the coroutine and then set it there, but I couldnt figure it out. I tried just changing the position after etc after the game managers state to restart gets called but the position change occurs before the main menu comes back on because its not being used in the coroutine.
Once thing I though would work, is i created a new method,
public void PlayerReset (){
if (GameManager.instance.Restart()){
//put new changes to player here.
}
}
The errors I came across here was I could not convert type void to bool, I assume its cause I was trying to say if the restart instance existed then function, but the way the restart function is created isn't true or false its just - is.
I really appreciate any help. I think what im going to try and do is make another script to the side and have it call the class of player to pull the components, and then manipulate them from there maybe. AUGH. So confusing. Lmao.
You can just set a public Transform variable on your object, and then in the inspector, drag the selected transform you want to call to that variable.
When that's done, you can use that transform variable in any way you want.
You can save the start position of the player in GameManager as it's a singleton. So, you set the position of the player to this saved position after restart.
You'll need to reinitialize all the variables(i.e. gameEnd,gameStarted, playerActive, gameOver etc.) on restart.
if(GameManager.instance.Restart()) will not work, as Restart() returns void not a boolean.
I can't really figure out how to solve my problem. I have been looking for an answer but I couldn't find anything.
I have a button in my scene that can be pressed both by client and host. When the button is pressed, it creates a cube in the scene. The problem is that: the cube can be created only by the host and the host is the only user that can see it and manipulate it.
My code is:
public class CreateCube : NetworkBehaviour {
GameObject cubo;
float lastCollisionTime=0;
float collisionTime=0;
void OnCollisionExit(Collision other) {
collisionTime = Time.time;
if (collisionTime - lastCollisionTime >1.5) {
CmdCreaCubo ();
lastCollisionTime = collisionTime;
}
}
}
}
[Command]
void CmdCreaCubo(){
GameObject cubo=Instantiate(Resources.Load("MyPrefabs\\Oggetti\\CubeGrasp")) as GameObject;
cubo.transform.position = new Vector3 (-5.88f, 7.51f, -19f);
cubo.name = "CubeGrasp";
NetworkServer.Spawn (cubo);
}
}
Could anyone help me please?
Thank you so much
Instead using simple Instantiate you should need to use Network.Instantiate
The given prefab will be instanted on all clients in the game.
Synchronization is automatically set up so there is no extra work
involved.
I am trying to play a particle effect when an enemy is killed but it seems to play on a randomly selected one rather than the one that was hit. However the enemy that was hit still disappears and still add points to the score.
At the moment I have three scripts to carry this out (All have been shortened so I'm only showing the relevant code):
One which is attached to boxes that are thrown at enemies that detects if they have collided with an enemy prefab.
void OnCollisionEnter (Collision theCollision) {
if (canProjectileKill == true) {
// If the projectile hits any game object with the tag "Enemy" or "EnemyContainer".
if (theCollision.gameObject.tag == "Enemy") {
GameObject.Find("EnemyExplosion").GetComponent<enemyDeath>().ProjectileHasHitEnemy();
// Destroy the projectile itself.
Destroy (gameObject);
// Destroy the game object that the projectile has collided with (E.g. the enemy).
Destroy (theCollision.gameObject);
GameObject.Find("Manager").GetComponent<projectileSpawner>().deleteProjectile();
}
}
}
Another that is attached to the enemy prefabs which detects if they have been hit by a box.
void OnCollisionEnter (Collision theCollision) {
if(theCollision.gameObject.tag == "Projectile") {
GameObject.Find("EnemyExplosion").GetComponent<enemyDeath>().EnemyHasBeenHit();
}
}
I then run an if statement asking if both the box has hit the enemy prefab AND if the enemy prefab has been hit by the box in an attempt to identify a single prefab rather than all of them. However this still doesn't work.
public bool HasProjectileHitEnemy;
public bool HasEnemyBeenHitByProjectile;
void Start () {
gameObject.particleSystem.Stop();
HasProjectileHitEnemy = false;
HasEnemyBeenHitByProjectile = false;
}
public void ProjectileHasHitEnemy () {
// From projectile.
HasProjectileHitEnemy = true;
}
public void EnemyHasBeenHit () {
// From enemy.
HasEnemyBeenHitByProjectile = true;
PlayParticleSystem();
}
public void PlayParticleSystem () {
if (HasEnemyBeenHitByProjectile == true && HasProjectileHitEnemy == true) {
gameObject.particleSystem.Play();
HasProjectileHitEnemy = false;
HasEnemyBeenHitByProjectile = false;
}
}
}
I am aware this is a long question but I have been stuck on this for over a week, so any help would be much appreciated. Thank you :)
I'm not sure what kind of object EnemyExplosion is, but your problem seems to be the search for this object. During OnCollisionEnter you know exactly between which objects the collision occurred. But now you're starting a search for any object that is called EnemyExplosion. That's the reason your particles appear random.
Update:
Ok, with your structure something like that
EnemyContainer
- EnemyExplosion
- Particle System
- EnemyModel
- Collider
If EnemyModel contains the collider, you can get to EnemyExplosion and finally enemyDeath the following way.
var explosion = theCollision.transform.parent.gameObject.GetComponent<EnemyExplosion>();
explosion.GetComponent<enemyDeath>().ProjectileHasHitEnemy();
Now that you're accessing the correct object, you can remove some of your double checks and rely on one collider event.
I seem to have found a way around this. Instead I've just set it to instantiate the particle system whenever an enemy detects that it has collided with a projectile. I then use a Coroutine to delete the particle system 2 seconds after.