I am doing a 2d game and I want my player to be able to change bullets when he collides with an object(power) and destroy that object. I have a script and I was thinking that I need to implement 2 variables prefab ON/Off but now thinking much more I want to change with the help of a tag ( My player has in his script a public Rigidbody2D bullet) and this function
void Fire()
{
if (photonView.IsMine)
{
var firedBullet = Instantiate(bullet, barrel.position, barrel.rotation);
firedBullet.AddForce(barrel.up * bulletSpeed);
}
}
this is the script that I was working on for switching bullets but I think it will not work to change a bullet that I add in the inspector for the Character script , to disable from this script and add other bullet . How I can make it by tag?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WeaponSwitching : MonoBehaviour
{
[SerializeField] private GameObject pickupEffect;
public GameObject[] DisablePrefab;
public GameObject[] EnablePrefab;
public int selectBullet = 0;
// Start is called before the first frame update
// Update is called once per frame
public void Bullet(Character bullet)
{
var effect = Instantiate(pickupEffect, transform.position, transform.rotation);
foreach (GameObject disable in DisablePrefab)
{
disable.SetActive(false);
}
foreach (GameObject enable in EnablePrefab)
{
enable.SetActive(true);
}
Destroy(gameObject);
Destroy(effect, 3.0f);
}
}
and I try this think with a BulletSwitch script to call the function from Weapon Switching script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletSwitch : MonoBehaviour
{
[SerializeField] private Character bullet;
// Start is called before the first frame update
private void Awake()
{
if (!bullet) bullet = GetComponent<Character>();
}
private void OnTriggerEnter2D(Collider2D other)
{
// or whatever tag your powerups have
if (!other.CompareTag("Bullet"))
{
Debug.LogWarning($"Registered a collision but with wrong tag: {other.tag}", this);
return;
}
var Bullet = other.GetComponent<WeaponSwitching>();
if (!Bullet)
{
Debug.LogError($"Object {other.name} is tagged PowerUp but has no PowerUp component attached", this);
return;
}
Debug.Log("Found powerup, pick it up!", this);
Bullet.Bullet(bullet);
}
}
inspector character
after my player collides with an object the bullets disappear.
You could make an array of prefabs and change to the correct bullet type when you hit a powerup by setting a CurrentBulletType variable to one of the types from the array. So when you hit powerup X, change bullet type to one of the types from the array.
public GameObject currentPrefab;
public GameObject[] bulletPrefabs;
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
bool foundObject = true;
switch (tagString)
{
case "standard bullet":
currentPrefab = bulletPrefabs[0];
break;
case "fancy bullet":
currentPrefab = bulletPrefabs[1];
break;
case "even fancier bullet":
currentPrefab = bulletPrefabs[2];
break;
default:
foundObject = false;
break;
}
if (foundObject) other.gameObject.SetActive(false);
}
You can instead use a dictionary to directly reference the correct prefab by string name (tag) to make it easy to add more bullet types.
//dictionairy to reference bullet types quickly
public Dictionary<string, BulletTypes> bulletLibrary = new Dictionary<string, BulletTypes>();
//set these in the inspector
public BulletTypes[] bulletTypes;
[System.Serializable]
public struct BulletTypes
{
public string bulletName;
public GameObject prefab;
//public int bulletPower; //more data if you wish
}
private void Start()
{
//fill the dictionary based on the filled bullet type array in inspector
for (int i = 0; i < bulletTypes.Length; i++)
{
if (bulletTypes[i].bulletName != "")
bulletLibrary.Add(bulletTypes[i].bulletName, bulletTypes[i]);
else
print("bullet added, but name empty");
}
}
private void OnTriggerEnter2D(Collider2D other)
{
string tagString = other.tag;
//if the collided tag is in the dictionary, we can reference the bullet type from that dictionairy
if (bulletLibrary.ContainsKey(tagString))
{
print("spawn: " + bulletLibrary[tagString].bulletName);
print("prefab: " + bulletLibrary[tagString].prefab);
//all the data you need is in: bulletLibrary[tagString];
//Bullet.Bullet(bulletLibrary[tagString]);
other.gameObject.SetActive(false);
}
}
Sidenote: If you spawn a lot of bullets, you can use pooling. SimplePool is nice and easy for this. Instead of calling: instantiate and destroy, you call: SimplePool.Spawn() and SimplePool.DeSpawn().
If you have any more questions on this feel free to ask ;)
Related
i have two simple 3d cube objects a,b. so my task is to randomly disable box collider one of the object every time game start.
my initial thought is randomly generate Boolean and if it is false then disable the box collider.
You can add you cube's to array and use Random.Range(0, yourArray.Length) to get index of the cube which should be deactivated. In your case it will be some overhead but this solution will be work for different cube`s count in the future.
In code it will looks like:
// You can serialize this array and add cubes from Inspector in Editor
// or add script with this code to the parent gameobject
// and use GetComponentsInChildren<BoxCollider>() to get all colliders
var cubes = new BoxCollider[2] {firstCube, secondCube};
var cubeToDeactivationIndex = Random.Range(0, cubes.Length);
cubes[cubeToDeactivationIndex].enabled = false;
About your second question. If I understand correctly, implementation will be next:
using System;
using UnityEngine;
using UnityEngine.Assertions;
using Random = UnityEngine.Random;
// Interface need to provide limited API
// without open references to gameobject and transform of the object
public interface ICollidable
{
event Action<ICollidable> OnCollided;
int HierarchyIndex { get; }
void DisableCollider();
}
// This component you should add to the object which will collide with player
public class CollidableObject : MonoBehaviour, ICollidable
{
[SerializeField]
private Collider _objectCollider;
public event Action<ICollidable> OnCollided;
public int HierarchyIndex => transform.GetSiblingIndex();
private void Start()
{
Assert.IsNotNull(_objectCollider);
}
//Here you can use your own logic how to detect collision
//I written it as example
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.CompareTag("Player")) {
OnCollided?.Invoke(this);
}
}
public void DisableCollider()
{
_objectCollider.enabled = false;
}
}
// This component should be on the parent gameobject for your `
// collidable objects. As alternative you can serialize array
// and add all collidable objects from the Inspector but in that
// case signature of the array should be CollidableObject[]
// because Unity can`t serialize interfaces
public class RamdomizedActivitySwitcher : MonoBehaviour
{
private ICollidable[] _collidableObjects;
private void Awake()
{
_collidableObjects = GetComponentsInChildren<ICollidable>(true);
foreach (var collidable in _collidableObjects)
{
collidable.OnCollided += DisableObjectWhenMatch;
}
}
private void DisableObjectWhenMatch(ICollidable collidedObject)
{
var randomIndex = Random.Range(0, _collidableObjects.Length);
if (randomIndex == collidedObject.HierarchyIndex) {
collidedObject.DisableCollider();
}
}
}
}
I'm trying to make a animated UI and I wanna destroy the object after the player left the Collider and I don't really seem to find a way to do that.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.InputSystem;
public class Crate : MonoBehaviour
{
public float duration;
public GameObject animateObject;
public GameObject crateBox;
public Text crateText;
public GameObject crateImage;
public GameObject crate;
public bool playerInRange;
private bool crateOpened = false;
// Update is called once per frame
void Update()
{
if (crateOpened == false)
{
if (Keyboard.current.eKey.wasPressedThisFrame && playerInRange)
{
if (crateBox.activeInHierarchy)
{
crateBox.SetActive(false);
}
else
{
crateBox.SetActive(true);
crateOpened = true;
Animate();
}
}
if (crateOpened == true)
{
Destroy(animateObject);
}
}
}
private void OnTriggerEnter2D(Collider2D other) {
if(other.CompareTag("Player")) {
playerInRange = true;
crateImage.SetActive(true);
}
}
private void OnTriggerExit2D(Collider2D other) {
if(other.CompareTag("Player")) {
playerInRange = false;
crateBox.SetActive(false);
crateImage.SetActive(false);
}
}
private void Animate()
{
LeanTween.scale(animateObject, new Vector3(1f, 1f, 1f), duration);
}
}
I'm trying to make a animated UI and I wanna destroy the object after the player left the Collider and I don't really seem to find a way to do that.
I think the following is in the wrong place:
if (crateOpened == true)
{
Destroy(animateObject);
}
Try moving this inside the if statement in OnTriggerExit2D. What you are doing now is destroying your animateObject before it is animated. What I think you want is for animateObject to be destroyed when the player leaves the collider.
Hello so I've been looking for a solution for my problem but looks like there is absolutely nothing about it.
I'm working on a scene where I have some 3D object renderred on a ground plane and my goal is making an animation on that 3D object start by tapping it. I'm using latest version of vuforia 10.4 with Unity 2020.3.9f1. I have a script for instantiating the 3d Model and making the plane finder disappear as long as it doesn't lose tracking.`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class sceneManager : MonoBehaviour
{
private string level = "snake";
public GameObject[] renderredPrefab;
public GameObject ground;
public GameObject groundFinder;
private int levelChecker(string levelName)
{
if (levelName == "snake")
return 0;
else return 1;
}
public void spawnObject(int i)
{
Instantiate(renderredPrefab[levelChecker(level)], new Vector3(0, 0, 0), Quaternion.identity, ground.transform);
}
public void groundFinderOff()
{
groundFinder.SetActive(false);
}
public void groundFinderOn()
{
groundFinder.SetActive(true);
}
}
And another one to trigger the animation following the game object's tag hereusing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class animationTriggerManager : MonoBehaviour
{
private Animator m_Animator;
private string objectName;
private GameObject[] eos;
private GameObject snake;
[SerializeField]
private Camera ARCamera;
// Start is called before the first frame update
void Start()
{
// Get the different eos present on the scene
for (int i = 0; i < eos.Length; i++)
{
eos[i] = GameObject.FindWithTag("myEolienne" + i);
}
// Get snake game objecct in the scene
snake = GameObject.FindWithTag("Snake");
}
// Update is called once per frame
void Update()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = ARCamera.ScreenPointToRay(Input.GetTouch(0).position);
if (Physics.Raycast(ray, out RaycastHit hit))
{
objectName = hit.collider.name;
Debug.Log("raycast touched " + hit.transform.name);
switch (objectName) //Get the Animator depending on which gameObject been tapped on.
{
case "myEolienne1":
m_Animator = eos[0].GetComponent<Animator>();
// Launch the animation on the gameObject that's been tapped
m_Animator.SetTrigger("Helice_Rotate");
Debug.Log("rotate launched");
break;
case "myEolienne2":
m_Animator = eos[1].GetComponent<Animator>();
// Launch the animation on the gameObject that's been tapped
m_Animator.SetTrigger("Helice_Rotate");
Debug.Log("rotate launched");
break;
case "myEolienne3":
m_Animator = eos[2].GetComponent<Animator>();
// Launch the animation on the gameObject that's been tapped
m_Animator.SetTrigger("Helice_Rotate");
Debug.Log("rotate launched");
break;
case "Snake":
m_Animator = snake.GetComponent<Animator>();
m_Animator.SetTrigger("snakeMoving");
break;
}
}
}
}
}
`
Note that each 3D model has different parts grouped in one parent that has a mesh collider on the parent only.enter image description here
The rendering works perfectly but I can't figure out what's wrong with my raycasting script. Note that I first tried with 3D model on image target and it worked fine.
Thanks in advance !
I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
public class PozicijaButona : MonoBehaviour
{
Vector2 pozicijaV;
float x, y;
Text tekstPozicije;
List<Vector2> lista = new List<Vector2>();
List<Sprite> slike1t, slike2t, slike3t, slike4t;
public GameObject instantacija;
private GameObject oznacenaSl;
Vector3 skala;
// Start is called before the first frame update
void Start()
{
tekstPozicije = GameObject.Find("Canvas/pozicija").GetComponent<Text>();
List<Vector2> lista = new List<Vector2>();
slike1t= new List<Sprite>();
for (int i = 0; i < HintoviMale.slikeT.Count; i++)
{
slike1t.Add(HintoviMale.slikeT[i]);
}
skala = transform.localScale;
instantacija.transform.localScale = skala;
Debug.Log("sjaa" + skala);
}
// Update is called once per frame
void Update()
{
}
private void OnMouseDown()
{
pozicijaV = transform.position;
x = transform.position.x;
y = transform.position.y;
string xT = x.ToString();
string yT = y.ToString();
// tekstPozicije.text=xT+","+yT;
tekstPozicije.text = pozicijaV.ToString();
Destroy(oznacenaSl);
}
public void klikNaPoziciju()
{
Debug.Log("broj itema u slici"+HintoviMale.slikeT.Count);
oznacenaSl = Instantiate (instantacija, new Vector2(-0.7f, -3.4f), Quaternion.identity);
// oznacenaSl.transform.localScale = skala;
oznacenaSl.GetComponent<SpriteRenderer>().sprite=HintoviMale.slikeT[0];
}
}
I did it many times,but maybe i forgot.I am trying to instantiate prefab,and scale it down but it doesnt take any effects.It only works if I scale original prefab,but then I am not able to Destroy this prefab.
You never call your function named "klikNaPoziciju", which contains the Instantiate call. You also have commented out the line that changes the localScale of the object named "oznacenaSl".
Here is a simple example of instantiating an object and modifying it's localScale:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void Awake()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = new Vector3(2f, 2f, 1f);
}
}
In the Unity Hierarchy, create an empty gameObject and attach the script to it. Then in the Inspector for the gameObject you just created, drag your prefab into the "Object to Instantiate" field.
EDIT:
OP mentioned they are calling their public method from an OnClick method in the Unity Editor. I'm not familiar with that approach, but another approach would be to use the OnMouseDown() function in your script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InstantiateObjectExample : MonoBehaviour
{
public GameObject objectToInstantiate;
private GameObject instantiatedObject;
private void OnMouseDown()
{
CreateInstanceOfObject();
}
private void CreateInstanceOfObject()
{
Debug.Log("Creating instance!");
instantiatedObject = Instantiate(objectToInstantiate, transform.position, Quaternion.identity);
instantiatedObject.transform.localScale = transform.localScale;
}
}
For this to work, make sure the object you attach the script to has a collider attached to it. Clicking on the collider will trigger the OnMouseDown() function.
I am experiencing the strangest issue
I have a ray cast and when it touches a certain layer it calls my function which does a small animation.
The problem is, this only works on a single object, I have tried duplicating, copying the prefab, dragging prefab to the scene, it doesn't work.
Now I have this code below, and as you can see I have this line which allows me to access the script on public PlatformFall platfall; so I can call platfall.startFall();
Something I've noticed, If I drag a single item from the hierarchy to the public PlatFall in Inspector then that SINGLE object works as it should. ( in that it animates when startFall is called). HOWEVER, if I drag the prefab from my project to the inspector then they do not work. (Even if debug log shows that the method is called animation does not occur).
public class CharacterController2D : MonoBehaviour {
//JummpRay Cast
public PlatformFall platfall;
// LayerMask to determine what is considered ground for the player
public LayerMask whatIsGround;
public LayerMask WhatIsFallingPlatform;
// Transform just below feet for checking if player is grounded
public Transform groundCheck;
/*....
...*/
Update(){
// Ray Casting to Fallingplatform
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall.startFall();
}
Debug.Log(isFallingPlatform);
}
}
Platform Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformFall : MonoBehaviour
{
public float fallDelay = 0.5f;
Animator anim;
Rigidbody2D rb2d;
void Awake()
{
Debug.Log("Awake Called");
anim = GetComponent<Animator>();
rb2d = GetComponent<Rigidbody2D>();
}
private void Start()
{
Debug.Log("Start Called");
}
//void OnCollisionEnter2D(Collision2D other)
//{
// Debug.Log(other.gameObject.tag);
// GameObject childObject = other.collider.gameObject;
// Debug.Log(childObject);
// if (other.gameObject.CompareTag("Feet"))
// {
// anim.SetTrigger("PlatformShake");
// Invoke("Fall", fallDelay);
// destroy the Log
// DestroyObject(this.gameObject, 4);
// }
//}
public void startFall()
{
anim.SetTrigger("PlatformShake");
Invoke("Fall", fallDelay);
Debug.Log("Fall Invoked");
// destroy the Log
// DestroyObject(this.gameObject, 4);
}
void Fall()
{
rb2d.isKinematic = false;
rb2d.mass = 15;
}
}
I understood from your post that you are always calling PlatformFall instance assigned from inspector. I think this changes will solve your problem.
public class CharacterController2D : MonoBehaviour {
private PlatformFall platfall;
private RaycastHit2D isFallingPlatform;
void FixedUpdate(){
isFallingPlatform = Physics2D.Linecast(_transform.position, groundCheck.position, WhatIsFallingPlatform);
if (isFallingPlatform)
{
Debug.Log("Here");
platfall = isFallingPlatform.transform.GetComponent<PlatformFall>();
platfall.startFall();
}
}
}
By the way, i assume that you put prefab to proper position to cast. And one more thing, you should make physics operations ,which affect your rigidbody, in FixedUpdate.