Creating a color loop effect in Unity? - c#

I'm currently developing a game in Unity and I ran into a small problem. I'm trying to generate a rainbow color effect that is similar to what happens when someone catches a star in mario kart or supermario on a specific gameobject. Essentially all the instances of the prefab attached to the script InteractControl that are active should get this effect (Either loop or lerp trough 5 different colors continusesly) while the variable timeLeft > 0 and the score > 10 and then go back to the original value of the color they had when timeLeft < 0.
Ive already tried multiple things including using a coroutine, and reseting the color inisde of it multiple times. Creating a new method and allowing acces to it only if a bool whom's value is set within the timeleft > 0 if statement is true, or currently trying to lerp the color however this all doesnt seem the work. Trying to make an animation could also be an option however its too complicated to make the objects of different colours match the same dimensions as the original I have so I really want to make it change trough the script.
These are the relevant parts of the script in question:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InteractControl : MonoBehaviour, IPooledObject
{
public Transform[] spawnPoints;
public Color[] _colors;
private float t = 0.2f;
private Rigidbody2D rb;
GameObject target;
Vector3 directionToTarget;
public static int LevelStart = 0;
public static float moveSpeed = 5f;
public static bool isSlowMotion = true;
public static float timeLeft = 12f;
private float randomSpawnTime;
private int Spawn = 1;
private float scaledTime;
private Color oldColor;
private Color newColor;
private float newT;
private void Start()
{
scaledTime = Mathf.Sin(t) * (float)(5 - 1);
oldColor = _colors[(int)scaledTime];
newColor = _colors[(int)(scaledTime + 1f)];
newT = scaledTime - Mathf.Round(scaledTime);
}
public void OnObjectSpawn()
{
target = GameObject.FindWithTag("White Ball");
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if (target != null)
{
if (ScoreScript.scoreValue > 10)
{
timeLeft -= Time.deltaTime;
if (timeLeft > 0)
{
directionToTarget = (target.transform.position - transform.position).normalized;
rb.velocity = new Vector2(directionToTarget.x * moveSpeed,
directionToTarget.y * moveSpeed);
GetComponent<Renderer>().material.color = Color.Lerp(oldColor, newColor, 5f);
if (Spawn == 1)
{
randomSpawnTime = Random.Range(2, 8);
Spawn++;
}
if (timeLeft < randomSpawnTime && Spawn <= 2)
{
BallSpawnerControl.ClockSpawn = true;
}
}
if (timeLeft < 0)
{
GameObject[] gos = GameObject.FindGameObjectsWithTag("ColouredBall Highress");
foreach (GameObject go in gos)
{
go.SetActive(false);
}
BallSpawnerControl.HeartSpawn = true;
}
}
}
}
}

Related

How do remove speed decreasing after a jump that is used AddForce method (Unity)?

I'm starting to make my game and ran into a problem with movement. My movement is based on the new input system and .AddForce(). The problem is that when I jump, the character's speed drops after landing and then starts picking up again.
A Physic Material with a Dynamic Friction of 2 hangs on the player's Capsule Collider (if it affects the problem)
How to make the character move without slowing down?
Video demonstration (I hope you can see what the problem is).
Rigidbody and Player Mover settings.
Code (there is no more associated code):
using System;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class PlayerMover : MonoBehaviour
{
[SerializeField] private LayerMask _groundLayer = 6;
[SerializeField] private float _moveForce;
[SerializeField] private float _maxForce;
[SerializeField] private float _jumpForce;
[SerializeField] private float _mouseSensitivity;
private PlayerInput _input;
private Rigidbody _rb;
private CapsuleCollider _collider;
private Camera _camera;
private float _rotationX;
private const float MinAngle = -90f;
private const float MaxAngle = 90f;
private void Awake()
{
_input = new PlayerInput();
_rb = GetComponent<Rigidbody>();
_collider = GetComponent<CapsuleCollider>();
_camera = GetComponentInChildren<Camera>();
_rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
if (_groundLayer == gameObject.layer)
{
Debug.LogError("Сортувальний рівень гравця повинен відрізнятися від сортувального рівня землі!");
}
_input.Player.Jump.performed += context => Jump();
}
private void OnEnable()
{
_input.Enable();
}
private void OnDisable()
{
_input.Disable();
}
private void FixedUpdate()
{
Move();
}
private void LateUpdate()
{
Look();
}
private bool IsGrounded
{
get
{
var bottomCenterPoint = new Vector3(_collider.bounds.center.x, _collider.bounds.center.y,
_collider.bounds.center.z);
return Physics.CheckCapsule(_collider.bounds.center, bottomCenterPoint, _collider.bounds.size.x * 1.1f,
_groundLayer);
}
}
private Vector3 MovementVector
{
get
{
var cameraDirectionF = _camera.transform.forward;
var cameraDirectionR = _camera.transform.right;
cameraDirectionF.y = 0;
cameraDirectionR.y = 0;
cameraDirectionF = cameraDirectionF.normalized;
cameraDirectionR = cameraDirectionR.normalized;
var direction = _input.Player.Move.ReadValue<Vector2>();
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
private Vector2 MouseVector => _input.Player.Look.ReadValue<Vector2>() * _mouseSensitivity * Time.deltaTime;
private void Jump()
{
if (IsGrounded == false) return;
_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
}
private void Move()
{
_rb.AddForce(MovementVector * _moveForce, ForceMode.Impulse);
if (Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2)) < _maxForce) return;
var maxVelocity = _rb.velocity.normalized * _maxForce;
_rb.velocity = new Vector3(maxVelocity.x, _rb.velocity.y, maxVelocity.z);
}
private void Look()
{
_rotationX -= MouseVector.y;
_rotationX = Math.Clamp(_rotationX, MinAngle, MaxAngle);
_camera.transform.localEulerAngles = new Vector3(_rotationX, MouseVector.y, 0);
transform.Rotate(Vector3.up * MouseVector.x);
}
}
I tried changing the different .AddForce() values in the .Move() and .Jump() methods. Changed the value when assigning Vector3 to _rb.velocity in the .Move() method, where there is a check for the maximum allowable speed (without this check, .AddForce() will constantly increase the speed of movement).
I also thought that the problem was in the MovementVector property, but it is not so, it gives the correct values.
(The following statement is most likely incorrect)
In my opinion, after the jump, at the lowest point of the jump (almost near the ground), the character falls perpendicularly down and thus the speed is completely extinguished, so it needs to be regained.
Edit: I just modified the code to see the lowest velocity values in the console. Added the following: two variables and .Update(), one check in the MovementVector property, and changing the _flag in the .Jump() method.
private bool _flag;
private double _min = double.MaxValue;
private void Update()
{
if (!_flag) return;
var current = Math.Sqrt(Math.Pow(_rb.velocity.x, 2) + Math.Pow(_rb.velocity.z, 2));
if (current < _min) _min = current;
Debug.Log(_min);
}
ㅤ
private Vector3 MovementVector
{
get
{
....
var direction = _input.Player.Move.ReadValue<Vector2>();
if (direction.magnitude == 0)
{
_flag = false;
}
return cameraDirectionF * direction.y + cameraDirectionR * direction.x;
}
}
ㅤ
private void Jump()
{
_flag = true;
if (IsGrounded == false) return;
//_rb.AddForce(new Vector3(0, _jumpForce, 0), ForceMode.Impulse);
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
}
Now, on landing, I will get accurate velocity values. With this, I noticed something, when changing _maxForce to a higher side, the landing velocity also changes. If for _maxForce equal to five the velocity dropped to zero, for _maxForce equal to 10 it drops already to 4, for _maxForce equal to 15 - to 9.
In Jump() Method you are adding the force of (0,_jumpforce,0) so this zeroes out the current movement. instead of zero you should addforce with the vector direction multiplied by jumpForce
Example:
_rb.AddForce(transform.up * _jumpForce, ForceMode.Impulse);
I hope this is fix works fine as per your requirement.

C# code works in one script, but not the other

I am currently learning C# as I'm studying a Games Design Course at University. I am currently implementing a system where: when the time reaches 0, the game pauses and ends. This works :).
However, at first I tried implementing the code into another script - but doesn't work on that script. This has puzzled me. Here is the code (that works):
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Health : MonoBehaviour
{
private Text TimeText;
public float HealthTimer;
public bool TimeIsRunning;
public GameObject CanvasEnd;
void Start()
{
TimeText = GameObject.Find("Timer").GetComponent<Text>();
TimeIsRunning = true;
CanvasEnd.SetActive(false);
}
void Update()
{
TimeText.text = HealthTimer.ToString("0");
if (TimeIsRunning == true)
{
if (HealthTimer > 0)
{
HealthTimer -= 1 * Time.deltaTime;
}
else
{
Time.timeScale = 0;
CanvasEnd.SetActive(true);
}
if(HealthTimer == 0)
{
TimeIsRunning = false;
HealthTimer = 0;
}
}
This script is directly attached to the timer. But I don't understand why it doesn't work when I implement it into another script (that already controls other game aspects). There were no errors, and everything was declared properly.
The exact same code was put into this script (in void update) and didn't work:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public GameObject Player;
public GameObject PlayerTracker;
public GameObject BlueBottle;
public GameObject GreenBottle;
public GameObject RedBottle;
public GameObject ChestCanvas;
public float Score;
public float ChestScoreK = 0;
private Text HT;
private Text HC;
private Text Port;
private Text TimeText;
float speed = 4;
float rotSpeed = 80;
float rot = 0f;
float gravity = 8;
// Variables counting Blue, Red and Green bottles
public int BBCounter = 0;
public int RBCounter = 0;
public int GBCounter = 0;
public int CCounter = 0;
Vector3 moveDir = Vector3.zero;
CharacterController controller;
Animator anim;
void Start()
{
controller = GetComponent<CharacterController>();
anim = GetComponent<Animator>();
ChestCanvas.SetActive(false);
TimeText = GameObject.Find("Timer").GetComponent<Text>();
}
void Update()
{
//Script was implemented here
if (controller.isGrounded)
{
if (Input.GetKey(KeyCode.W))
{
anim.SetInteger("condition", 1);
moveDir = new Vector3(0, 0, 1);
moveDir *= speed;
moveDir = transform.TransformDirection (moveDir);
}
if (Input.GetKeyUp(KeyCode.W))
{
anim.SetInteger("condition", 0);
moveDir = new Vector3(0, 0, 0);
}
}
rot += Input.GetAxis("Horizontal") * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, rot, 0);
moveDir.y -= gravity * Time.deltaTime;
controller.Move(moveDir * Time.deltaTime);
}
private void FixedUpdate()
{
PlayerTracker.transform.position = Player.transform.position;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "BlueBottle")
{
Debug.Log("BLUE");
BBCounter = BBCounter + 1;
GameObject MasterScriptBlue = GameObject.Find("GameMaster");
MasterScriptBlue.GetComponent<GameMasterScript>();
MasterScriptBlue.GetComponent<GameMasterScript>().BlueBottleCounter = BBCounter;
}
if (other.gameObject.tag == "RedBottle")
{
Debug.Log("RED");
RBCounter = RBCounter + 1;
GameObject MasterScriptRed = GameObject.Find("GameMaster");
MasterScriptRed.GetComponent<GameMasterScript>();
MasterScriptRed.GetComponent<GameMasterScript>().RedBottleCounter = RBCounter;
}
if (other.gameObject.tag == "GreenBottle")
{
Debug.Log("GREEN");
GBCounter = GBCounter + 1;
GameObject MasterScriptGreen = GameObject.Find("GameMaster");
MasterScriptGreen.GetComponent<GameMasterScript>();
MasterScriptGreen.GetComponent<GameMasterScript>().GreenBottleCounter = GBCounter;
}
if(other.gameObject.tag == "CityCollider")
{
GameObject HuntingCity = GameObject.Find("HC");
Destroy(HuntingCity);
ScoringSystem.theScore += 100;
Destroy(other.gameObject);
Debug.Log("Destroyed");
}
if(other.gameObject.tag == "TownCollider")
{
GameObject HuntingTown = GameObject.Find("HT");
Destroy(HuntingTown);
ScoringSystem.theScore += 100;
Destroy(other.gameObject);
Debug.Log("Destroyed");
Debug.Log("Destroyed");
}
if(other.gameObject.tag == "PortCollider")
{
GameObject Port = GameObject.Find("Port");
Destroy(Port);
ScoringSystem.theScore += 100;
Destroy(other.gameObject);
Debug.Log("Destroyed");
}
if(other.gameObject.tag == "Chest")
{
Time.timeScale = 0;
ChestCanvas.SetActive(true);
ChestScoreK += 1;
GameObject MasterScriptChest = GameObject.Find("GameMaster");
MasterScriptChest.GetComponent<GameMasterScript>().ChestScore += ChestScoreK;
Destroy(other.gameObject);
GameObject HealthScript = GameObject.Find("Timer");
HealthScript.GetComponent<Health>().TimeIsRunning = false;
}
}
}
What can I try next?
It's hard to understand the problem from your question, but if you are looking for potential problems in your script then it looks like you are comparing a float to zero here:
if(HealthTimer == 0)
{
TimeIsRunning = false;
HealthTimer = 0;
}
So it is possible that HealthTimer is never exactly zero.
You probbaly want if (HealthTimer <= 0f) instead, or to move that block into the else block of your preceding if statement.
Other differences with the second script are:
the second script is not setting TimeIsRunning = true; in the Start() method,
other methods set Time.timeScale = 0; in the second script (not sure what impact this has, but may be relevant).
Do you have the same 'using' tags at the top of your scripts? The 'text' class only exists in UnityEngine.UI, and not UnityEngine.

How to program a bunch of 1st person animations at different speeds

I have made 4 different types of animation clips in an animator for an an empty gameobject called "Animator". The main camera is a child of this.
The animations feature a running cycle, a walking cycle, a crouch cycle, and an idle cycle. They all have a trigger that let's them play.
How am I able to measure the speed of the player and execute these animations when the player reaches a certain speed. I have found someone else trying to do this and it works but only for idle and walk. But unfortunately I can't get the sprint and crouch to work. I'm not sure what to do, either have the sprint and crouch animations or just change the speed of the walk animation depending on whether the player is sprinting or crouching. I'll leave a comment where the code I found is.
Here's what I have in my player controller (thge trigger stop is for the idle animation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
Animator _ar;
public float speed;
[Range(-5, -20)]
public float gravity = -9.81f;
public float sprintSpeed = 6f;
public float walkSpeed = 4f;
public float crouchSpeed = 2f;
public float standHeight = 1.6f;
public float crouchHeight = 1f;
Vector3 velocity;
bool isGrounded;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public Light _l;
//Set this to the transform you want to check
public Transform objectTransfom;
private float noMovementThreshold = 0.0001f;
private const int noMovementFrames = 1;
Vector3[] previousLocations = new Vector3[noMovementFrames];
public bool isMoving;
//Let other scripts see if the object is moving
public bool IsMoving
{
get { return isMoving; }
}
void Awake()
{
//For good measure, set the previous locations
for (int i = 0; i < previousLocations.Length; i++)
{
previousLocations[i] = Vector3.zero;
}
}
void Start()
{
_ar = GameObject.Find("Animator").GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
//Below here is the code I found. The if statements for isMoving, is what I put in to see if
//this worked.
//Store the newest vector at the end of the list of vectors
for (int i = 0; i < previousLocations.Length - 1; i++)
{
previousLocations[i] = previousLocations[i + 1];
}
previousLocations[previousLocations.Length - 1] = objectTransfom.position;
//Check the distances between the points in your previous locations
//If for the past several updates, there are no movements smaller than the threshold,
//you can most likely assume that the object is not moving
for (int i = 0; i < previousLocations.Length - 1; i++)
{
if (Vector3.Distance(previousLocations[i], previousLocations[i + 1]) >= noMovementThreshold)
{
//The minimum movement has been detected between frames
isMoving = true;
break;
}
else
{
isMoving = false;
}
}
if(isMoving == true)
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
speed = sprintSpeed;
_ar.SetTrigger("WalkSprint");
}
else if (Input.GetKeyUp(KeyCode.LeftControl))
{
speed = crouchSpeed;
_ar.SetTrigger("WalkCrouch");
//transform.localScale = new Vector3(0.8f, 0.5f, 0.8f);
}
else
{
speed = walkSpeed;
_ar.SetTrigger("Walk");
//transform.localScale = new Vector3(0.8f, 0.85f, 0.8f);
}
}
else
{
_ar.SetTrigger("Stop");
}
}
}
Unfortunately, as with many issue in Game Dev, this could be a number of different issues (or all of them!). Here is where you can start to debug:
Check your error log to see if there is anything obvious that jumps out at you, like a bad reference to a Game Object/Componenet.
Watch the animator. You should be able to have the Animator open, side-by-side with your game window while the game is running. You should be able to see the animations running and transitioning. See if something is not linked properly, or if an animation time is configured incorrectly, etc.
Add some debug statements, like outputting the current trigger being set. I would even verify that your inputs are configured correctly. Maybe add some additional conditionals at the beginning that debug what inputs are being pressed.
As #OnionFan said, your Input check is for KeyUp for the Crouch key. That should probably be KeyDown.

Setting the spawn position for a 3D object

I am making an endless runner style game in unity where the floor tiles spawn randomly and endlessly in front of the player as they run and delete themselves after a certain distance behind the player this is all working fine and as intended however the individual tiles spawn about half way inside each other and as much as I try to debug my code I can't seem to effect them. Ideally, I want the code to do exactly what it's doing, but the tiles spawn end to end rather than inside each other.
Any help would be greatly appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tile_Manager : MonoBehaviour
{
public GameObject[] tilePrefabs;
private Transform playerTransform;
private float spawnZ = 5.0f;
private float tileLength = 5.0f;
private float safeZone = 7.0f;
private int amtTilesOnScreen = 10;
private int lastPrefabIndex = 0;
private List<GameObject> activeTiles;
// Use this for initialization
void Start () {
activeTiles = new List<GameObject>();
playerTransform = GameObject.FindGameObjectWithTag ("Player").transform;
for (int i = 0; i < amtTilesOnScreen; i++)
{
if (i < 2)
SpawnTile(0);
else
SpawnTile();
}
}
// Update is called once per frame
void Update () {
if (playerTransform.position.z - safeZone > (spawnZ - amtTilesOnScreen * tileLength))
{
SpawnTile();
DeleteTile();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
if (prefabIndex == -1)
go = Instantiate(tilePrefabs[RandomPrefabIndex()]) as GameObject;
else
go = Instantiate(tilePrefabs[prefabIndex]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
activeTiles.Add (go);
}
private void DeleteTile()
{
Destroy(activeTiles [0]);
activeTiles.RemoveAt (0);
}
private int RandomPrefabIndex()
{
if (tilePrefabs.Length <= 1)
return 0;
int randomIndex = lastPrefabIndex;
while (randomIndex == lastPrefabIndex)
{
randomIndex = Random.Range(0, tilePrefabs.Length);
}
lastPrefabIndex = randomIndex;
return randomIndex;
}
}
stacked tiles
You need to take the length of a tile into account. Try changing this
go.transform.position = Vector3.forward * spawnZ;
to this
go.transform.position = Vector3.forward * (spawnZ + tileLength / 2);
to add half the tile length to the spawn position.
Wouldn't you want
go.transform.Translate(Vector3.forward * spawnZ);
not position?
As you're spawning things relative to the world coordinate system.
https://docs.unity3d.com/ScriptReference/Transform.Translate.html

Simple resetting timer method to help lerp between a materials alpha values 1 - 0

I am working on a 'Shield' script (sci-fi, not RPG, imagine a spaceship) that only becomes active once a collision has happened. I'm trying to achieve this by having the alpha value of the material be set to 0f - then once 'hit' going straight up to 1f and then lerping back down to 0f. Rinse, repeat. However i'm having a problem coming up with the timer element of the lerp. The timers i've looked at tend to involve the update method or do not allow me to use it in the lerp. This is my code so far:
using UnityEngine;
using System.Collections;
public class Shield : MonoBehaviour {
public int shieldHealth;
private SpriteRenderer sRend;
private float alpha = 0f;
void Start () {
shieldHealth = 10;
sRend = GetComponentInChildren<SpriteRenderer>();
}
void OnCollisionEnter2D (Collision2D other) {
shieldHealth --;
LerpAlphaOfShield ();
if(shieldHealth <= 0) {
Destroy(this.gameObject);
}
}
void LerpAlphaOfShield () {
float lerp = Mathf.SmoothStep(1.0f, 0.0f, /*missing timer*/);
alpha = Mathf.Lerp(0.0f, 1.0f, lerp);
sRend.material.color = new Color(1, 1, 1, alpha);
}
}
Now i've thought that the SmoothStep part may be unnecessary once I have a timer, however this part I found in another question that was originally a PingPong that allowed the alpha values to go back and forth: I was trying to get it to do it just once then, obviously, reset with a timer.
Thanks in advance for any advice!
SOLUTION
I finally came up with this to reset and trigger again every hit and turn the alpha values of the shield from 1 - 0 in 1 second. If anyone thinks this code that I have could be improved please let me know, I would be very interested!
using UnityEngine;
using System.Collections;
public class Shield : MonoBehaviour {
public int shieldHealth;
private SpriteRenderer sRend;
private float alpha = 0f;
private bool hasCollided = false;
private float timer = 1f;
void Start () {
shieldHealth = 10;
sRend = GetComponentInChildren<SpriteRenderer>();
}
void Update () {
timer -= Time.deltaTime;
if(timer >= 0.001) {
alpha = Mathf.Lerp(0.0f, 1.0f, timer);
sRend.material.color = new Color(1, 1, 1, alpha);
}
if (timer <= 0) {
hasCollided = false;
}
}
void OnCollisionEnter2D (Collision2D other) {
shieldHealth --;
hasCollided = true;
timer = 1f;
if(shieldHealth <= 0) {
Destroy(this.gameObject);
}
}
}
A possible solution is to do this:
public float lerpSpeed = 5f;
float currAlpha;
float goal = 0f;
void Update()
{
currAlpha = Mathf.Lerp(currAlpha, goal, lerpSpeed * Time.deltaTime;
sRend.material.color = new Color(1f, 1f, 1f, alpha);
if(1f - currAlpha > .001f)
{
goal = 0f;
}
}
And then in the OnCollisionEnter2D function set goal = 1f;
lerpSpeed is a variable you can play with to change how fast it lerps.

Categories