Modify prefab ingame, revert back when game ends - c#

In my game, there are multiple types of projectiles. I have projectiles from the player and also for some enemies. But all these projectiles are directed from the same script.
In order for each projectile to behave differently, I made a prefab for each which instantiates when that "weapon" triggers it. This way each prefab has its own statistics of damage and other components.
Inside the game I want the user to be able to change the prefabs "Damage on hit", but this needs to be reverted back once the user is dead and restarts the game.
So my question is, within my upgrade menu how can I change the value of DamageOnhit for each different projectile since these are all attached to different weapons.
My 3 different projectile prefabs (Going to be more in the future)
The int counter of each prefab
My projectile causes each different projectile to start with standard damage of 1.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {
[Header ("Speed")]
public float baseSpeed;
public float randomSpeed;
public Vector2 SpeedV2;
public Vector2 Direction;
[Header ("Damage")]
public int DamageOnHit = 1;
[Header ("Layers")]
public LayerMask solid_layer;
public LayerMask entities_layer;
[Header ("OnHit FX")]
public GameObject HitFxPrefab;
public GameObject DustFxPrefab;
[Header ("Bounce")]
public bool BounceOnCollide = false;
public int bouncesLeft = 0;
[HideInInspector]
public Health owner; // owner of the projectile
private Vector2 Position; // Current position
private Vector2 movementCounter = Vector2.zero; // Counter for subpixel movement
public BoxCollider2D myCollider;
List<Health> healthsDamaged = new List<Health>(); // List to store healths damaged
void Awake () {
if (myCollider == null) {
myCollider = GetComponent<BoxCollider2D> ();
}
}
void Start () {
// keeping everything Pixel perfect
Position = new Vector2 (Mathf.Round(transform.position.x), Mathf.Round(transform.position.y));
transform.position = Position;
}
void Update () {
SpeedV2 = new Vector2 (transform.right.x, transform.right.y) * (baseSpeed + Random.value * randomSpeed) * Time.deltaTime;
}
void LateUpdate () {
if (SpeedV2.x != 0) {
MoveH (SpeedV2.x);
}
if (SpeedV2.y != 0) {
MoveV (SpeedV2.y);
}
}
void DestroyMe () {
if (HitFxPrefab != null) {
var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
h.transform.localScale = transform.lossyScale;
h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
}
Destroy (gameObject);
}
void DestroyMeWall () {
if (HitFxPrefab != null) {
var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
h.transform.localScale = transform.lossyScale;
h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
}
Destroy (gameObject);
}
public void BounceHorizontal () {
bouncesLeft--;
transform.right = new Vector3 (-transform.right.x, transform.right.y, transform.right.z);
SpeedV2 *= 0.8f;
}
public void BounceVertical () {
bouncesLeft--;
transform.right = new Vector3 (transform.right.x, -transform.right.y, transform.right.z);
SpeedV2 *= 0.8f;
}
void OnCollideWith (Collider2D col, bool horizontalCol = true) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
return;
}
}
// if the projectile hit's a solid object, destroy it
if (col.gameObject.layer == (int)Mathf.Log(solid_layer.value, 2)) {
DestroyMeWall ();
return;
}
}
void OnCollideWithEntity(Collider2D col) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
}
}
}
// Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
public bool MoveH(float moveH) {
this.movementCounter.x = this.movementCounter.x + moveH;
int num = (int)Mathf.Round(this.movementCounter.x);
if (num != 0)
{
this.movementCounter.x = this.movementCounter.x - (float)num;
return this.MoveHExact(num);
}
return false;
}
// Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
public bool MoveV(float moveV) {
this.movementCounter.y = this.movementCounter.y + moveV;
int num = (int)Mathf.Round(this.movementCounter.y);
if (num != 0)
{
this.movementCounter.y = this.movementCounter.y - (float)num;
return this.MoveVExact(num);
}
return false;
}
// Function to move the Actor Horizontally an exact integer amount
public bool MoveVExact(int moveV) {
int num = (int)Mathf.Sign((float)moveV);
while (moveV != 0) {
bool solid = CheckColInDir(Vector2.up * (float)num, solid_layer);
if (solid) {
if (BounceOnCollide && bouncesLeft > 0) {
bouncesLeft--;
num = -num;
moveV = -moveV;
BounceVertical ();
} else {
this.movementCounter.x = 0f;
DestroyMeWall ();
return true;
}
}
bool entity = CheckColInDir(Vector2.up * (float)num, entities_layer);
if (entity) {
var entit = CheckColsInDirAll (Vector2.up * (float)num, entities_layer);
OnCollideWithEntity (entit [0]);
}
moveV -= num;
transform.position = new Vector2 (transform.position.x, transform.position.y + (float)num);
}
return false;
}
// Function to move the Actor Horizontally an exact integer amount
public bool MoveHExact(int moveH) {
int num = (int)Mathf.Sign((float)moveH);
while (moveH != 0) {
bool solid = CheckColInDir(Vector2.right * (float)num, solid_layer);
if (solid) {
if (BounceOnCollide && bouncesLeft > 0) {
bouncesLeft--;
num = -num;
moveH = -moveH;
BounceHorizontal ();
} else {
this.movementCounter.x = 0f;
DestroyMeWall ();
return true;
}
}
bool entity = CheckColInDir(Vector2.right * (float)num, entities_layer);
if (entity) {
var entit = CheckColsInDirAll (Vector2.right * (float)num, entities_layer);
OnCollideWithEntity (entit [0]);
}
moveH -= num;
transform.position = new Vector2 (transform.position.x + (float)num, transform.position.y);
}
return false;
}
// Helper function to check if there is any collision within a given layer in a set direction (only use up, down, left, right)
public bool CheckColInDir (Vector2 dir, LayerMask layer) {
Vector2 leftcorner = Vector2.zero;
Vector2 rightcorner = Vector2.zero;
if (dir.x > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.x < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.y > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
} else if (dir.y < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
}
return Physics2D.OverlapArea(leftcorner, rightcorner, layer);
}
// The same as CheckColInDir but it returns a Collider2D array of the colliders you're collisioning with
public Collider2D[] CheckColsInDirAll (Vector2 dir, LayerMask layer) {
Vector2 leftcorner = Vector2.zero;
Vector2 rightcorner = Vector2.zero;
if (dir.x > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.x < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.y > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
} else if (dir.y < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
}
return Physics2D.OverlapAreaAll(leftcorner, rightcorner, layer);
}
}
UpgradeMenu
public void UpgradeDamage ()
{
Projectile.DamageOnHit += 1;
// ScoreManager.Score -= upgradeCost;
UpdateValues();
}
I want to be able to upgrade this value for each different prefab.
I tried this with changing the DamageOnHit to a static, but once I upgrade this value. All the projectiles get upgraded. This is NOT what I want since I want to individually be able to change each prefab.

You create a BulletManager.cs (attach this to an object that will be always active or an empty gameobject)
public static BulletManager instance;
private void Awake
{
if ( instance == null ) //this creates a Singleton so you can access it directly from everywhere, won't go deep into explaining how it works exactly
{
instance = this;
}
else
{
Destroy (gameObject);
}
}
public int machinegunDmg; //set the initial values in the editor
public int shotgunDmg;
public int skeletonDmg;
Now tag all your prefabs with appropriate tags, let's say you use a "MachineGunProj" tag for the machinegun projectile prefab.
The same script you have attached to all prefabs should get the damage from that BulletManager script depending on what prefab you instantiate.
private int DamageOnHit;
//this will get called everytime you instantiate a new prefab that holds this script; it will check for its own tag and depending on it will set the damage in this script to be equal to the appropriate value from BulletManager.cs
private void Start
{
if(this.gameObject.CompareTag("MachineGunProj"))
{
this.DamageOnHit = BulletManager.instance.machinegunDmg;
}
else if(this.gameObject.CompareTag("ShotgunProj"))
{
this.DamageOnHit = BulletManager.instance.shotgunDmg;
}
//else if -- do the same for every prefab you have
}
As for the upgrades, you would need to change the values in BulletManager.cs.
For example:
public void UpgradeMachineGun()
{
BulletManager.instance.machinegunDmg++; //next time you spawn a machinegun prefab it will take the upgraded value
}
*I wrote the code above directly here without the assistance of any text editors or whatever so there is a chance that I have probably missed something but overall this is the idea of how it should work. If something doesn't work I would be more than glad to give you further assistance :)

Two alternatives:
Set DamageOnHit on each instance of the projectile
Each time you Instantiate a new projectile prefab, get its Projectile component and set its DamageOnHit to the desired value.
.
Each time the game restarts, make a copy of each prefab asset
We will call them "ProjectileShotgunProto" and "ProjectileSkeletonProto". You will call Instantiate(ProjectileShotgunProto) them when the player shoots, instead of instantiating your original prefab.
In any case, do not alter the original prefab assets from your code, that will lead to issues.

Related

Why the object keep moving up nonstop and how to move it forward fast when it finished moving up?

I want to move the object up smooth slowly from it's current position on y 50.01 to new position 51.255
But the object keep moving up nonstop.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class RoboSphereWindowBreakInteraction : MonoBehaviour
{
public Transform target;
public AudioClip audioClip;
public float speed;
private bool hasStarted = false;
private Animator anim;
void Update()
{
if ((Input.GetKeyDown(KeyCode.B) || (hasStarted == true)))
{
float step = speed * Time.deltaTime; // calculate distance to move
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
hasStarted = true;
}
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "Square 1")
{
GetComponent<Rigidbody>().isKinematic = false;
hasStarted = false;
Destroy(GameObject.Find("Wall_Window_Long_03"));
}
}
public void ActivateRoboSphere()
{
foreach(Transform child in transform)
{
if(child.name == "Camera")
{
RepositionCamera(child);
}
}
anim = GetComponent<Animator>();
anim.enabled = true;
GetComponent<FPEInteractableActivateScript>().interactionString = "";
FPEInteractionManagerScript.Instance.BeginCutscene();
StartCoroutine(PlayAudio());
}
private void RepositionCamera(Transform camera)
{
var Eyes = GameObject.Find("eyeDome");
camera.position = Eyes.transform.position + Eyes.transform.forward;
camera.LookAt(Eyes.transform);
camera.GetComponent<Camera>().enabled = true;
}
IEnumerator PlayAudio()
{
AudioSource audio = GetComponent<AudioSource>();
audio.clip = audioClip;
audio.Play();
yield return new WaitForSeconds(audio.clip.length);
var rotation = Quaternion.LookRotation(target.position - transform.position);
StartCoroutine(Spin(3f, rotation, () =>
{
anim.SetBool("Roll_Anim", true);
}));
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
}
IEnumerator Spin(float lerpTime, Quaternion rotation, Action whenDone)
{
float elapsedTime = 0f;
while (elapsedTime <= lerpTime)
{
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, elapsedTime / lerpTime);
elapsedTime += Time.deltaTime;
yield return null;
}
whenDone?.Invoke();
}
// 51.255
IEnumerator MoveFromTo(Transform objectToMove, Vector3 a, Vector3 b, float speed)
{
float step = (speed / (a - b).magnitude) * Time.fixedDeltaTime;
float t = 0;
while (t <= 1.0f)
{
t += step; // Goes from 0 to 1, incrementing by step each time
objectToMove.position = Vector3.Lerp(a, b, t); // Move objectToMove closer to b
yield return new WaitForFixedUpdate(); // Leave the routine and return here in the next frame
}
objectToMove.position = b;
}
}
I did :
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
But the object keep moving up nonstop. Or at least very high and not like I wanted from 50.01 to 51.255
And when the object reaching the height of 51.255 then I want it to move fast smooth forward to target.
In your code replace this part:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
transform.position.y + 51.255f, transform.position.z), 3f));
with this:
StartCoroutine(MoveFromTo(transform, transform.position, new Vector3(transform.position.x,
51.255f, transform.position.z), 3f));

Differentiate Enemy Projectile from User's Projectile

I have a user who fires a projectile to damage enemies.
Although some enemies have a projectile of their own to damage the player. They both come from the same projectile script.
The problem is that when the user upgrades his projectile (with the in-game currency). Both the user's and the enemies projectile get upgraded. Since it's from the same script, it updates the DamageOnHit of the projectile.
Now my question is: am I able to tell my script to only upgrade the user's projectiles, without creating a separate enemy projectile script?
The projectile code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {
[Header ("Speed")]
public float baseSpeed;
public float randomSpeed;
public Vector2 SpeedV2;
public Vector2 Direction;
[Header ("Damage")]
public static int DamageOnHit = 1;
[Header ("Layers")]
public LayerMask solid_layer;
public LayerMask entities_layer;
[Header ("OnHit FX")]
public GameObject HitFxPrefab;
public GameObject DustFxPrefab;
[Header ("Bounce")]
public bool BounceOnCollide = false;
public int bouncesLeft = 0;
[HideInInspector]
public Health owner; // owner of the projectile
private Vector2 Position; // Current position
private Vector2 movementCounter = Vector2.zero; // Counter for subpixel movement
public BoxCollider2D myCollider;
List<Health> healthsDamaged = new List<Health>(); // List to store healths damaged
void Awake () {
if (myCollider == null) {
myCollider = GetComponent<BoxCollider2D> ();
}
}
void Start () {
// keeping everything Pixel perfect
Position = new Vector2 (Mathf.Round(transform.position.x), Mathf.Round(transform.position.y));
transform.position = Position;
}
void Update () {
SpeedV2 = new Vector2 (transform.right.x, transform.right.y) * (baseSpeed + Random.value * randomSpeed) * Time.deltaTime;
}
void LateUpdate () {
if (SpeedV2.x != 0) {
MoveH (SpeedV2.x);
}
if (SpeedV2.y != 0) {
MoveV (SpeedV2.y);
}
}
void DestroyMe () {
if (HitFxPrefab != null) {
var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
h.transform.localScale = transform.lossyScale;
h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
}
Destroy (gameObject);
}
void DestroyMeWall () {
if (HitFxPrefab != null) {
var h = Instantiate (HitFxPrefab, transform.position, transform.rotation);
h.transform.localScale = transform.lossyScale;
h.transform.localRotation = Quaternion.Euler (new Vector3(0f, 0f, Random.value * 360f));
}
Destroy (gameObject);
}
public void BounceHorizontal () {
bouncesLeft--;
transform.right = new Vector3 (-transform.right.x, transform.right.y, transform.right.z);
SpeedV2 *= 0.8f;
}
public void BounceVertical () {
bouncesLeft--;
transform.right = new Vector3 (transform.right.x, -transform.right.y, transform.right.z);
SpeedV2 *= 0.8f;
}
void OnCollideWith (Collider2D col, bool horizontalCol = true) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
return;
}
}
// if the projectile hit's a solid object, destroy it
if (col.gameObject.layer == (int)Mathf.Log(solid_layer.value, 2)) {
DestroyMeWall ();
return;
}
}
void OnCollideWithEntity(Collider2D col) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
}
}
}
// Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
public bool MoveH(float moveH) {
this.movementCounter.x = this.movementCounter.x + moveH;
int num = (int)Mathf.Round(this.movementCounter.x);
if (num != 0)
{
this.movementCounter.x = this.movementCounter.x - (float)num;
return this.MoveHExact(num);
}
return false;
}
// Function to move the Actor Horizontally, this only stores the float value of the movement to allow for subpixel movement and calls the MoveHExact function to do the actual movement
public bool MoveV(float moveV) {
this.movementCounter.y = this.movementCounter.y + moveV;
int num = (int)Mathf.Round(this.movementCounter.y);
if (num != 0)
{
this.movementCounter.y = this.movementCounter.y - (float)num;
return this.MoveVExact(num);
}
return false;
}
// Function to move the Actor Horizontally an exact integer amount
public bool MoveVExact(int moveV) {
int num = (int)Mathf.Sign((float)moveV);
while (moveV != 0) {
bool solid = CheckColInDir(Vector2.up * (float)num, solid_layer);
if (solid) {
if (BounceOnCollide && bouncesLeft > 0) {
bouncesLeft--;
num = -num;
moveV = -moveV;
BounceVertical ();
} else {
this.movementCounter.x = 0f;
DestroyMeWall ();
return true;
}
}
bool entity = CheckColInDir(Vector2.up * (float)num, entities_layer);
if (entity) {
var entit = CheckColsInDirAll (Vector2.up * (float)num, entities_layer);
OnCollideWithEntity (entit [0]);
}
moveV -= num;
transform.position = new Vector2 (transform.position.x, transform.position.y + (float)num);
}
return false;
}
// Function to move the Actor Horizontally an exact integer amount
public bool MoveHExact(int moveH) {
int num = (int)Mathf.Sign((float)moveH);
while (moveH != 0) {
bool solid = CheckColInDir(Vector2.right * (float)num, solid_layer);
if (solid) {
if (BounceOnCollide && bouncesLeft > 0) {
bouncesLeft--;
num = -num;
moveH = -moveH;
BounceHorizontal ();
} else {
this.movementCounter.x = 0f;
DestroyMeWall ();
return true;
}
}
bool entity = CheckColInDir(Vector2.right * (float)num, entities_layer);
if (entity) {
var entit = CheckColsInDirAll (Vector2.right * (float)num, entities_layer);
OnCollideWithEntity (entit [0]);
}
moveH -= num;
transform.position = new Vector2 (transform.position.x + (float)num, transform.position.y);
}
return false;
}
// Helper function to check if there is any collision within a given layer in a set direction (only use up, down, left, right)
public bool CheckColInDir (Vector2 dir, LayerMask layer) {
Vector2 leftcorner = Vector2.zero;
Vector2 rightcorner = Vector2.zero;
if (dir.x > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.x < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.y > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
} else if (dir.y < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
}
return Physics2D.OverlapArea(leftcorner, rightcorner, layer);
}
// The same as CheckColInDir but it returns a Collider2D array of the colliders you're collisioning with
public Collider2D[] CheckColsInDirAll (Vector2 dir, LayerMask layer) {
Vector2 leftcorner = Vector2.zero;
Vector2 rightcorner = Vector2.zero;
if (dir.x > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x + .5f, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.x < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x - .5f, myCollider.bounds.center.y + myCollider.bounds.extents.y - .1f);
rightcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x, myCollider.bounds.center.y - myCollider.bounds.extents.y + .1f);
} else if (dir.y > 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y + .5f);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y + myCollider.bounds.extents.y);
} else if (dir.y < 0) {
leftcorner = new Vector2 (myCollider.bounds.center.x - myCollider.bounds.extents.x + .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y);
rightcorner = new Vector2 (myCollider.bounds.center.x + myCollider.bounds.extents.x - .1f, myCollider.bounds.center.y - myCollider.bounds.extents.y - .5f);
}
return Physics2D.OverlapAreaAll(leftcorner, rightcorner, layer);
}
}
UpgradeMenu Code. As you can see I upgrade the DamageOnHit from the projectile script. That's how ALL projectiles get more damage.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UpgradeMenu : MonoBehaviour
{
[SerializeField]
private Text accuracyText;
[SerializeField]
private Text speedText;
[SerializeField]
private Text damageText;
[SerializeField]
private Weapon weapon;
[SerializeField]
public Projectile projectile;
[SerializeField]
private Player player;
[SerializeField]
private int upgradeCost = 50;
void start ()
{
}
void OnEnable()
{
UpdateValues();
}
void UpdateValues ()
{
}
public void UpgradeArmor ()
{
Health.maxHealth += 2;
ScoreManager.Score -= upgradeCost;
UpdateValues();
}
public void UpgradeSouls ()
{
EnemySlime.ScoreOnDeath += 1;
EnemySkeleton.ScoreOnDeath += 1;
// ScoreManager.Score -= upgradeCost;
UpdateValues();
}
public void UpgradeDamage ()
{
Projectile.DamageOnHit += 1;
// ScoreManager.Score -= upgradeCost;
UpdateValues();
}
}
in what you are trying to achieve, you want to avoid static fields.
You should be able to work with something like :
player.projectile = new Projectile();
player.projectile.DamageOnHit = 5;
enemy.projectile = new Projectile();
enemy.projectile.DamageOnHit = 1;
Hope this helps
#YouneS answer involves using base classes, this is a better solution but it does mean you'll need a total of 3 classes. 1 base class holding the standard projectile logic and 2 other classes for the player and enemy projectile inheriting the projectile base class. If you don't know what inheritance is here is a link for you: https://www.tutorialspoint.com/csharp/csharp_inheritance
There's also a different way of doing this. You could create an enum called ProjectileType in the projectile class like so:
public Enum ProjectileType { PlayerProjectile, EnemyProjectile }
And assign this ProjectileType to a projectileType variable whenever you create/instantiate a projectile like so:
private projectileType;
public Projectile(ProjectileType projectileType)
{
this.projectileType = projectileType;
}
Now whenever you upgrade a projectile you can check whether the projectile is of player type like so:
if(projectileType == ProjectileType.PlayerProjectile)
{
// Upgrade logic.
}
I still think YouneS's answer is smarter but it's totally up to you!
I am not sur if that is the best solution, because it all depend on your game logic, but you could create two classes of projectiles, one for enemies and one for the player. Both classes can inherite from your Projectile class.
public class PlayerProjectil : Projectile {...}
public class AIProjectil : Projectile {...}
Make Projectile class functions virtual. and override them inside PlayerProjectil and AIProjectil when you need different behaviour.

Unity 2018.1.3f1 Player's Rigidbody2D and Box Collider 2D are not colliding with collidable tiles

I cannot seem to find a way to collide the player with objects that are supposed to stop the player. For example, when I move the player up to a wall of trees, it just moves through them (and bounces away from the grid alignment slightly) like the player were a ghost.
And below is the code I'm using as far as Player's movement is concerned:
using System.Collections;
using UnityEngine;
public class PlayerMovement : Character {
Direction currentDir;
Vector2 input;
bool IsMoving = false;
Vector3 startpos;
Vector3 endpos;
float t;
public Sprite upsprite;
public Sprite rightsprite;
public Sprite downsprite;
public Sprite leftsprite;
public float walkSpeed = 3f;
public bool isAllowedToMove = true;
private Rigidbody2D myRB;
void Start()
{
isAllowedToMove = true;
myRB = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
protected override void Update () {
if (! IsMoving && isAllowedToMove) {
input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
if (Mathf.Abs(input.x) > Mathf.Abs(input.y))
input.y = 0;
else
input.x = 0;
if (input != Vector2.zero) {
if (input.x < 0) {
currentDir = Direction.Left;
}
if (input.x > 0) {
currentDir = Direction.Right;
}
if (input.y < 0) {
currentDir = Direction.Down;
}
if (input.y > 0) {
currentDir = Direction.Up;
}
switch (currentDir) {
case Direction.Up:
gameObject.GetComponent<SpriteRenderer>().sprite = upsprite;
break;
case Direction.Right:
gameObject.GetComponent<SpriteRenderer>().sprite = rightsprite;
break;
case Direction.Down:
gameObject.GetComponent<SpriteRenderer>().sprite = downsprite;
break;
case Direction.Left:
gameObject.GetComponent<SpriteRenderer>().sprite = leftsprite;
break;
}
StartCoroutine (Move (transform));
}
myRB.velocity = new Vector2 (Input.GetAxisRaw ("Horizontal") * walkSpeed, myRB.velocity.y);
myRB.velocity = new Vector2 (myRB.velocity.x, Input.GetAxisRaw ("Vertical") * walkSpeed);
if (Input.GetAxisRaw ("Horizontal") < 0.5f && Input.GetAxisRaw ("Horizontal") > -0.5f)
{
myRB.velocity = new Vector2 (0f, myRB.velocity.y);
}
if (Input.GetAxisRaw ("Vertical") < 0.5f && Input.GetAxisRaw ("Vertical") > -0.5f)
{
myRB.velocity = new Vector2(myRB.velocity.y, 0f);
}
}
base.Update ();
}
public IEnumerator Move(Transform entity)
{
IsMoving = true;
startpos = entity.position;
t = 0;
endpos = new Vector3(startpos.x + System.Math.Sign(input.x), startpos.y + System.Math.Sign(input.y), startpos.z);
while(t < 1f)
{
t += Time.deltaTime * walkSpeed;
entity.position = Vector3.Lerp(startpos, endpos, t);
yield return null;
}
IsMoving = false;
yield return 0;
}
}
enum Direction
{
Up,
Right,
Down,
Left
}
Other things to note are that I made sure to use Rigidbody2D and Box Collider2D components, and I made sure to make the Gravity scale 0 for the player and froze the rotation from the Z axis. Also, I did use Tiled and Tiled2Unity extensions to create the tiles you see in game, despite the preinstalled tilemap assets being a decent choice to create 2d game worlds.
Forgive me if it seems too long, but I can't seem to figure out what the problem is with the collision detection. Can anyone help?

Unity2D Vector2 does not work

So I'm making a Crossy Road clone in Unity 2D and I get an error on line 34.
I'm not sure what I have missed but I need another perspective.
To be specific I'm getting an error on this statement:
if (transform.position != new Vector2(curPosition.x, transform.position.y) + nextDir)
and everything is marked red.
public class PlayerController : MonoBehaviour
{
public Vector2 nextDir;
public float jumpForce = 100;
public float speed = 5;
public float speedRot = 100;
public float rotationOffset;
Rigidbody rb;
public Vector2 curPosition;
void Start()
{
rb = GetComponent<Rigidbody>();
curPosition = transform.position;
}
void Update()
{
if (transform.position != new Vector2(curPosition.x, transform.position.y) + nextDir)
{
transform.position = Vector2.MoveTowards(transform.position, new Vector2(curPosition.x, transform.position.y) + nextDir, speed * Time.deltaTime);
}
else
{
nextDir = Vector2.zero;
curPosition = transform.position;
curPosition.x = Mathf.Round(curPosition.x);
curPosition.y = Mathf.Round(curPosition.y);
if (Input.GetAxisRaw("Horizontal") != 0)
{
nextDir.x = -Input.GetAxisRaw("Horizontal");
Move();
}
else if (Input.GetAxisRaw("Vertical") != 0)
{
nextDir.y = Input.GetAxisRaw("Vertical");
Move();
}
}
}
void Move()
{
rb.AddForce(0, jumpForce, 0);
}
}
First error ( about lack of Rigidbody2D component ) can be solved by adding RequireComponent attribute on top of the PlayerController :
[RequireComponent(typeof(Rigidbody2D)]
public class PlayerController : MonoBehaviour
{
// rest of your code
And the you're trying to compare object with 3 axis to the one with 2 axis
void Update()
{
// here you're trying to compare Vector3 ( transform.position )
// to Vector2, (3 axis with 2 axis)
//if (transform.position != new Vector2(curPosition.x, transform.position.y) + nextDir)
// change it to this
Vector2 cmp = new Vector2(curPosition.x, transform.position.y) + nextDir;
if(cmp[0] != transform.position[0] || cmp[1] != transform.position[1])
{
transform.position = Vector2.MoveTowards(transform.position, new Vector2(curPosition.x, transform.position.y) + nextDir, speed * Time.deltaTime);
}
// rest of the update's body
}
// rest of your code ...
}

Multiple raycast with restrictions

I have this problem which I have been trying to figure out for quite some time already. The code I have below is almost complete. I just need to add this additional feature it should work how I want it to work.
So what I want to implement into the code is the make alertedLock false when !withinRange.
But for some reason no matter how i do it. It doesnt work. Because the problem i have is that, when i implement some kind of code to do that, everything goes back to normal.
Thanks in advance.
Edit
The script should be doing this:
If player !withinRange && !withinAngle of enemy then, enemy.color.blue;
If player is not within Range for enemy to detect and player is not within certain Angle for enemy to detect. It means, player is not detected by enemy. So we use Color.Blue to represent that.
If player !withinRange && withinAngle of enemy then, enemy.color.blue;
If player is not within Range for enemy to detect and player is within Angle for enemy to detect. It means, player is still not detect by enemy. So we use Color.Blue to represent that.
If player withinRange && !withinAngle of enemy then, enemy.color.red;
If player is within Range for enemy to detect and player is not within Angle for enemy to detect player. It means, enemy has detected something within Range but doesnt meet all the requirements, that is, withinAngle = true. So in this case we use Color.Red to represent that.
If player withinRange && withinAngle of enemy then, enemy.color.green;
If player is within Range for enemy to detect and player is within Angle for enemy to detect player. It means, player has successfully killed the the enemy. Therefore, using Color.Green to represent that.
If player withinRange && touchRestrictedRaycast of enemy then, enemy.color.magenta (forever) unless !withinRange && !withinAnge
If player is within Range for enemy to detect and player is not within Angle for enemy to detect player. But however then hits the raycast, touchRestrictedRaycast. This will make a lock. So the player cant kill the enemy from a invalid position. So when this happens, the player can no longer kill the enemy. Unless the player is totally out of Range of the enemies detection.
The problem is 5. I dont know how to code 5.
Edit
When i tried to code it outside the forloop , that is,
if (alertedLock && !withinRange) {
alertedLock = false;
}
Does doesnt solve the problem. It returns the solution to where alertedLock is always false
Even when i try to apply it inside the for loop. Such like,
if(withinRange) {
// Inside
if(alertedLock) {
gameObject.renderer.material.color = Color.magenta;
}
if(!alertedLock) {
if(enemyAngleTLUP || enemyAngleTLLEFT) {
alertedLock = true;
}
if(withinAngle) {
gameObject.renderer.material.color = Color.green;
}
if(!withinAngle) {
gameObject.renderer.material.color = Color.red;
}
}
}
if (!withinRange){
if(alteredLock) {
alertedLock = false;
}
}
There is a problem when i do this, its because once it detects the first raycast detection. It ignores the rest, and so it has this color state problem.
I found out this problem on my earlier questions, here:
https://gamedev.stackexchange.com/questions/90329/raycast-flashing-problem
Edit
alertedLock is just a bool which determines when the player has touched the restricted raycast. Due to the fact the player can only kill the enemy from a certain range and angle (withinRange && withinAngle). This is why we have alertedLock.
However, when alertedLock is true. It can only be turned off when the player isnt within the kill range of the enemy (!withinRange)
using UnityEngine;
using System.Collections;
public class Script_v2 : MonoBehaviour {
// Player Properties
private GameObject player;
public Vector3 playerSize;
private Vector3 playerTransform;
public Vector3 playerTransformTL;
public Vector3 playerTransformTR;
public Vector3 playerTransformBL;
public Vector3 playerTransformBR;
private Vector3 newPlayerTransformTL;
private Vector3 newPlayerTransformTR;
private Vector3[] playerRaycastPoints;
// Enemy Properties
private Vector3 enemyTransformTL;
private Vector3 enemyTransformTR;
private Vector3 enemyTransformBL;
private Vector3 enemyTransformBR;
public float distance;
public Vector3 enemySize;
// Detection Alerts
public bool outOfVision;
public bool alerted;
public bool alertedLock;
public bool withinRange;
public bool withinAngle;
public bool dead;
Ray ray;
RaycastHit hit;
// Use this for initialization
void Start () {
playerRaycastPoints = new Vector3[4];
distance = 3f;
player = GameObject.FindGameObjectWithTag ("Player");
}
// Update is called once per frame
void Update () {
enemyTransformTL = new Vector3 (transform.position.x - 0.5f, transform.position.y + 0.5f, transform.position.z);
enemyTransformTR = new Vector3 (transform.position.x + 0.5f, transform.position.y + 0.5f, transform.position.z);
enemyTransform_TL_TR ();
Reference_Player_Transform_Points ();
Player_Transform_Points_Detection ();
Debug.Log (alerted + " " + alertedLock);
}
void OnDrawGizmos() {
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere (new Vector3(transform.position.x - 0.5f, transform.position.y + 0.5f, transform.position.z), distance);
//Gizmos.DrawWireSphere (new Vector3(transform.position.x + 0.5f, transform.position.y + 0.5f, transform.position.z), distance);
}
public void enemyTransform_TL_TR() {
if (alertedLock && !withinRange) {
alertedLock = false;
}
if (!alertedLock) {
gameObject.renderer.material.color = Color.blue;
}
for (int i = 0; i < playerRaycastPoints.Length; i++) {
double enemyAngleTL = Mathf.Atan2(playerRaycastPoints[i].y - ( transform.position.y + 0.5f ),
playerRaycastPoints[i].x - ( transform.position.x - 0.5f )) * 180f / 3.14159265f;
//Debug.Log (enemyAngleTL);
double enemyAngleTR = Mathf.Atan2 (playerRaycastPoints[i].y - (transform.position.y + 0.5f),
playerRaycastPoints[i].x - (transform.position.x + 0.5f)) * 180f / 3.14159265f;
Vector3 directionTL = (playerRaycastPoints[i] - enemyTransformTL).normalized;
Ray rayTL = new Ray(enemyTransformTL, directionTL);
RaycastHit hitTL;
Vector3 directionTR = (playerRaycastPoints[i] - enemyTransformTR).normalized;
Ray rayTR = new Ray (enemyTransformTR, directionTR);
RaycastHit hitTR;
withinRange = Physics.Raycast (rayTL, out hitTL, distance);
withinAngle = enemyAngleTL > 90 && enemyAngleTL < 180;
RaycastHit hitTPUP;
RaycastHit hitTPLEFT;
bool enemyAngleTLUP = Physics.Raycast(enemyTransformTL, Vector3.up, out hitTPUP, distance);
bool enemyAngleTLLEFT = Physics.Raycast(enemyTransformTL, Vector3.left, out hitTPLEFT, distance);
Debug.DrawRay(enemyTransformTL, Vector3.up * distance);
Debug.DrawRay(enemyTransformTL, Vector3.left * distance);
if(withinRange) {
// Inside
if(alertedLock) {
gameObject.renderer.material.color = Color.magenta;
}
if(!alertedLock) {
if(enemyAngleTLUP || enemyAngleTLLEFT) {
alertedLock = true;
}
if(withinAngle) {
gameObject.renderer.material.color = Color.green;
}
if(!withinAngle) {
gameObject.renderer.material.color = Color.red;
}
}
}
}
}
private void Reference_Player_Transform_Points() {
playerSize = player.transform.localScale;
playerTransformTL = new Vector3(player.transform.position.x - (playerSize.x / 2),
player.transform.position.y + playerSize.y / 2,
player.transform.position.z);
playerTransformTR = new Vector3(player.transform.position.x + (playerSize.x / 2),
player.transform.position.y + playerSize.y / 2,
player.transform.position.z);
playerTransformBL = new Vector3(player.transform.position.x - (playerSize.x / 2),
player.transform.position.y - playerSize.y / 2,
player.transform.position.z);
playerTransformBR = new Vector3(player.transform.position.x + (playerSize.x / 2),
player.transform.position.y - playerSize.y / 2,
player.transform.position.z);
playerRaycastPoints [0] = playerTransformTL;
playerRaycastPoints [1] = playerTransformTR;
playerRaycastPoints [2] = playerTransformBL;
playerRaycastPoints [3] = playerTransformBR;
/*
Debug.Log (playerTransformTL);
Debug.Log (playerTransformTR);
Debug.Log (playerTransformBL);
Debug.Log (playerTransformBR);
*/
}
private void Player_Transform_Points_Detection() {
float eTLpTL = Vector3.Distance (enemyTransformTL, playerTransformTL);
float eTLpTR = Vector3.Distance (enemyTransformTL, playerTransformTR);
float eTLpBL = Vector3.Distance (enemyTransformTL, playerTransformBL);
float eTLpBR = Vector3.Distance (enemyTransformTL, playerTransformBR);
float eTRpTL = Vector3.Distance (enemyTransformTR, playerTransformTL);
float eTRpTR = Vector3.Distance (enemyTransformTR, playerTransformTR);
float eTRpBL = Vector3.Distance (enemyTransformTR, playerTransformBL);
float eTRpBR = Vector3.Distance (enemyTransformTR, playerTransformBR);
float eTLMin = Mathf.Min (eTLpTL, eTLpTR, eTLpBL, eTLpBR);
if (eTLMin == eTLpTL) {
newPlayerTransformTL = playerTransformTL;
// Debug.Log("eTLpTL");
}
else if(eTLMin == eTLpTR) {
newPlayerTransformTL = playerTransformTR;
// Debug.Log("eTLpTR");
}
else if(eTLMin == eTLpBL) {
newPlayerTransformTL = playerTransformBL;
// Debug.Log("eTLpBL");
}
else if(eTLMin == eTLpBR) {
newPlayerTransformTL = playerTransformBR;
// Debug.Log("eTLpBR");
}
float eTRMin = Mathf.Min (eTRpTL, eTRpTR, eTRpBL, eTRpBR);
if(eTRMin == eTRpTL) {
newPlayerTransformTR = playerTransformTL;
// Debug.Log("eTRpTL");
}
else if(eTRMin == eTRpTR) {
newPlayerTransformTR = playerTransformTR;
// Debug.Log("eTRpTR");
}
else if(eTRMin == eTRpBL) {
newPlayerTransformTR = playerTransformBL;
// Debug.Log("eTRpBL");
}
else if(eTRMin == eTRpBR) {
newPlayerTransformTR = playerTransformBR;
// Debug.Log("eTRpBR");
}
}
}

Categories

Resources