Trying to get fish to face direction it's moving - c#

So far I'm making an app, and the early stages of it will just be a fish swimming around a small box. I made code that alters the fishes direction randomly and if it reaches certain x or y values to keep it within visibility of the camera. Unfortunately my C# knowledge is limited and I can't figure out how to make it turn the direction it is , this is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FishBehavior : MonoBehaviour {
public float speed = 2f;
float moveSpeed = 0f;
private Rigidbody2D rigid;
public float rotateSpeed = 2f;
public bool isPredator = false;
void Start () {
rigid = GetComponent<Rigidbody2D>();
rigid.velocity = new Vector2(speed, 0);
InvokeRepeating("FishMovement", 3f, Random.Range(2f,6f));
}
// Update is called once per frame
void Update () {
int num = Random.Range(1, 10);
if (transform.position.x >= 11.5)
{
speed = -2;
rigid.velocity = new Vector2(speed, 0);
}
if (transform.position.x <= -11.5)
{
speed = 2;
rigid.velocity = new Vector2(speed, 0);
}
if (transform.position.y >= 5.07)
{
rigid.velocity = new Vector2(Random.Range(-2, 2), -2);
}
if (transform.position.y <= -4.65)
{
rigid.velocity = new Vector2(Random.Range(-2, 2), 2);
}
}
void FishMovement()
{
int fishSpeed = Random.Range(-3, 3);
if (fishSpeed != 0)
{
moveSpeed = Random.Range(-2, 2);
rigid.velocity = new Vector2(fishSpeed, moveSpeed);
}
}
}
Anything helps thanks

Related

Unity - how to make an object face the direction of its movement?

I'm trying to make a character prefab face the direction in which its moving. I've tired all sorts of things, with and without rigidbodies but nothing seems to work. The thing is, it does actually face in the correct direction. But once its there, it starts to rotate the whole prefab and it goes down into the ground.
The character holds a 3D shield, and goes towards a tower. So once it reaches the tower it raises the shield which in turn rotates the whole character down into the ground. I would like it to just rotate in the X and Z axis and never change the Y axis.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlockRadar : MonoBehaviour
{
// Start is called before the first frame update
public Transform Tower;
private GameObject[] multipleBlocks;
public Transform closestBlock;
public bool blockContact;
public float currentDistance;
public float stopDistance;
public Animator animator;
public int damage;
public float attackspeed;
private float canAttack;
public float moveSpeed = 5f;
public Vector3 distance;
private Vector3 movement;
void Start()
{
closestBlock = null;
blockContact = false;
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.position;
pos.y = 0;
pos.x = 0;
transform.position = pos;
closestBlock = getClosestBlock();
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(1, 0.7f, 0, 1);
Vector3 direction = closestBlock.position - transform.position;
direction.Normalize();
movement = direction;
float dist = Vector3.Distance(closestBlock.position, transform.position);
if (dist <= 1.5f)
{
{
blockContact = true;
animator.SetBool("Moving", false);
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
if (dist > 1.5f)
{
transform.forward = movement;
blockContact = false;
Debug.Log("Lost contact with block");
animator.SetBool("Moving", true);
moveCharacter(movement);
}
}
public void Attack()
{
Debug.Log("ATTACKING!");
Damage(closestBlock.transform);
animator.SetTrigger("Attacking");
}
private void FixedUpdate()
{
}
void moveCharacter(Vector3 direction)
{
transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World);
}
void DistanceToTower()
{
if (Tower)
{
float dist = Vector3.Distance(Tower.position, transform.position);
if (dist <= 1)
{
{
blockContact = true;
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
}
}
//when the object carrying this script is destroyed
private void OnDestroy()
{
if (closestBlock !=null)
{
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(0, 0, 0, 0);
}
}
public Transform getClosestBlock()
{
multipleBlocks = GameObject.FindGameObjectsWithTag("Block");
float closestDistance = Mathf.Infinity;
Transform trans = null;
//finds all blocks in the scene
foreach (GameObject go in multipleBlocks)
{
currentDistance = Vector3.Distance(transform.position, go.transform.position);
if (currentDistance < closestDistance)
{
closestDistance = currentDistance;
trans = go.transform;
}
}
return trans;
}
void Damage(Transform block)
{
Tower_Stats e = block.GetComponent<Tower_Stats>();
if (e != null)
{
e.TakeDamage(damage);
}
}
}
I would be really, really grateful for any help. As I said before I used to have rigidbodies on the character, but I removed them since I thought maybe they were the fault. But doesnt seem like it. One other thing I've noticed is that when the prefab is instantiated, its children doesnt have the correct position values. Not sure why. But if that could be a clue I just thought I'd let you know.

Is there a way to make the four switchLane functions into one function and randomizing which lane it chooses to switch to? [duplicate]

This question already has answers here:
How do I call functions randomly unity c# [duplicate]
(2 answers)
Closed 2 years ago.
I am making a car game where the enemies have behaviour where to move to a specefic point on the road I already have the 4 points but I don't know how to randomize where the enemy prefab moves to I have to change it manually now. I would really appreciate some help and thank you already. I am a beginner at coding so take it easy ;)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyScript : MonoBehaviour
{
public float speed;
private GameObject playerTransform;
private GameObject enemyCarPrefab;
private float distanceToNextCar;
public float distance;
private float roadWidth;
private float lane1 = 7.25f;
private float lane2 = 2.45f;
private float lane3 = 2.4f;
private float lane4 = 7.3f;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
playerTransform = GameObject.FindGameObjectWithTag("Player");
enemyCarPrefab = GameObject.FindGameObjectWithTag("BaseCar");
distanceToNextCar = enemyCarPrefab.transform.position.z - distance;
if (playerTransform.transform.position.z > distanceToNextCar)
{
switchLane1();
}
}
private void switchLane1()
{
Vector3 targetPos1 = new Vector3(-lane1, 0, 0);
transform.position += (targetPos1 * speed * Time.deltaTime);
if (transform.position.x <= -lane1)
{
Vector3 newPositionLeft = new Vector3(-lane1, transform.position.y, transform.position.z);
transform.position = newPositionLeft;
}
else if (transform.position.x >= lane1)
{
Vector3 newPositionRight = new Vector3(lane1, transform.position.y, transform.position.z);
transform.position = newPositionRight;
}
}
private void switchLane2()
{
Vector3 targetPos2 = new Vector3(-lane2, 0, 0);
transform.position += (targetPos2 * speed * Time.deltaTime);
if (transform.position.x <= -lane2)
{
Vector3 newPositionLeft = new Vector3(-lane2, transform.position.y, transform.position.z);
transform.position = newPositionLeft;
}
else if (transform.position.x >= lane2)
{
Vector3 newPositionRight = new Vector3(lane2, transform.position.y, transform.position.z);
transform.position = newPositionRight;
}
}
private void switchLane3()
{
Vector3 targetPos3 = new Vector3(lane3, 0, 0);
transform.position += (targetPos3 * speed * Time.deltaTime);
if (transform.position.x <= -lane3)
{
Vector3 newPositionLeft = new Vector3(-lane3, transform.position.y, transform.position.z);
transform.position = newPositionLeft;
}
else if (transform.position.x >= lane3)
{
Vector3 newPositionRight = new Vector3(lane3, transform.position.y, transform.position.z);
transform.position = newPositionRight;
}
}
private void switchLane4()
{
Vector3 targetPos4 = new Vector3(lane4, 0, 0);
transform.position += (targetPos4 * speed * Time.deltaTime);
if (transform.position.x <= -lane4)
{
Vector3 newPositionLeft = new Vector3(-lane4, transform.position.y, transform.position.z);
transform.position = newPositionLeft;
}
else if (transform.position.x >= lane4)
{
Vector3 newPositionRight = new Vector3(lane4, transform.position.y, transform.position.z);
transform.position = newPositionRight;
}
}
}
You can use Random class in C# to generate a random value between 1 and 4, a switch statement can call any of the method based on the random value like below
var lane = new Random().Next(1, 5);
switch (lane)
{
case 1:
switchLane1();
break;
case 2:
switchLane2();
break;
case 3:
switchLane3();
break;
case 4:
switchLane4();
break;
default:
break;
}
This is a little standalone program that will help you understand how to achieve what you want to achieve:
using System;
class Program
{
static void Main()
{
// Here are all the lanes in an array
var lanes = new float[]{7.25f, 2.45f, 2.4f, 7.3f};
// Here is a random number generator
var rand = new Random();
// This will call 'switchLane' with one lane picked at random
switchLane(lanes[rand.Next(0,lanes.Length)]);
}
static void switchLane(float lane)
{
// This displays the lane, change to whatever you want to do with it
Console.WriteLine(lane);
}
}
Since a "lane" is such an important concept in your program, I'd give it its own class. Might look like this:
class Lane
{
public float Position { get; }
public Lane(float position) => this.Position = position;
public MoveInto(GameObject objectToMove)
{
Vector3 targetPos1 = new Vector3(this.Position, 0, 0);
objectToMove.Position += (targetPos1 * speed * Time.deltaTime);
if (objectToMove.Position <= this.Position)
{
//etc....
}
}
}
Then in your EnemyScript class you'd keep a list of these:
class EnemyScript : MonoBehavior
{
private Lane[] lanes = new Lane[]
{
new Lane(7.25f),
new Lane(2.45f),
new Lane(2.4f),
new Lane(7.3f)
};
Then to switch to one of the lanes, you'd just do this:
lanes[laneNumber - 1].MoveInto(player);

How can I rotate an object to the same direction only if it's twice in a row?

The object is rotating to both directions randomly. Right or left.
The range is between 90 and 270.
But I want to add a rule that if the object rotated for example twice in a row on the left direction or the right direction the next random number should on the other side. So the object can rotate randomly only twice in a row in the same side.
If rotated twice on the same side range the next it must be rotating randomly on the other side.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateRandom : MonoBehaviour
{
bool noTarget = true;
Quaternion qTo;
Quaternion[] qTos;
float rotateSpeed = 3.0f;
float timer = 0.0f;
private void Start()
{
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(90.0f, 270.0f), 0.0f));
qTos = new Quaternion[100];
for(int i = 0; i < qTos.Length; i++)
{
qTos[i] = Quaternion.Euler(new Vector3(0.0f, Random.Range(90.0f, 270.0f), 0.0f));
}
}
private void Update()
{
timer += Time.deltaTime;
if (noTarget == true)
{
if (timer > 2)
{ // timer resets at 2, allowing .5 s to do the rotating
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(90.0f, 270.0f), 0.0f));
timer = 0.0f;
}
transform.rotation = Quaternion.Slerp(transform.rotation, qTo, Time.deltaTime * rotateSpeed);
}
}
}
I tried to add a qTos array and maybe to make that rule already in the Start or using this array to make the rule inside the Update.
Or maybe to make the rule directly already in the Euler line in the timer :
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(90.0f, 270.0f), 0.0f));
I tried this but it's not working like I wanted it keep just rotating randomly like before :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateRandom : MonoBehaviour
{
bool noTarget = true;
Quaternion qTo;
float speed = 1.25f;
float rotateSpeed = 3.0f;
float timer = 0.0f;
int counter = 0;
private void Start()
{
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(90.0f, 270.0f), 0.0f));
}
private void Update()
{
timer += Time.deltaTime;
if (noTarget == true)
{
if (timer > 2)
{
var rand = Random.Range(90.0f, 270.0f);
counter += 1;
if (rand >= 90 && rand <= 180)
{
if(counter == 2)
{
rand = Random.Range(180.0f, 270.0f);
counter = 0;
}
}
if (rand >= 270 && rand >= 180)
{
if (counter == 2)
{
rand = Random.Range(90.0f, 180.0f);
}
counter = 0;
}
qTo = Quaternion.Euler(new Vector3(0.0f, rand, 0.0f));
timer = 0.0f;
}
transform.rotation = Quaternion.Slerp(transform.rotation, qTo, Time.deltaTime * rotateSpeed);
}
}
}

Why Isn't ray collisions working in unity

So I was working through this tutorial.
https://www.youtube.com/watch?v=OBtaLCmJexk
and I can't find the error in my code anywhere
The problem is just that after working on the vertical ray collisions I run the program and the block just falls through the obstacle.
here is the player code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Controller2D))]
public class Player : MonoBehaviour {
float gravity = -20;
Vector3 velocity;
Controller2D controller;
void Start() {
controller = GetComponent<Controller2D>();
}
void Update() {
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
and here is the Controller2D code:
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class Controller2D : MonoBehaviour {
public LayerMask collisionMask;
const float skinWidth = .015f;
public int horizontalRayCount = 4;
public int verticalRayCount = 4;
float horizontalRaySpacing;
float verticalRaySpacing;
BoxCollider2D collider;
RaycastOrigins raycastOrigins;
void Start() {
collider = GetComponent<BoxCollider2D>();
CalculateRaySpacing();
}
public void Move(Vector3 velocity) {
UpdateRaycastOrigins();
VerticalCollisions(ref velocity);
transform.Translate(velocity);
}
void VerticalCollisions(ref Vector3 velocity) {
float directionY = Mathf.Sign(velocity.y);
float rayLength = Mathf.Abs(velocity.y) + skinWidth;
for (int i = 0; i < verticalRayCount; i++) {
Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;
rayOrigin += Vector2.right * (verticalRaySpacing * i + velocity.x);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);
Debug.DrawRay(raycastOrigins.bottomLeft + Vector2.right * verticalRaySpacing * i, Vector2.up * -2, Color.red);
if (hit) {
velocity.y = (hit.distance - skinWidth) * directionY;
rayLength = hit.distance;
}
}
}
void UpdateRaycastOrigins() {
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
raycastOrigins.bottomLeft = new Vector2(bounds.min.x, bounds.min.y);
raycastOrigins.bottomRight = new Vector2(bounds.max.x, bounds.min.y);
raycastOrigins.topLeft = new Vector2(bounds.min.x, bounds.max.y);
raycastOrigins.topRight = new Vector2(bounds.max.x, bounds.max.y);
}
void CalculateRaySpacing() {
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
horizontalRayCount = Mathf.Clamp(horizontalRayCount, 2, int.MaxValue);
verticalRayCount = Mathf.Clamp(verticalRayCount, 2, int.MaxValue);
horizontalRaySpacing = bounds.size.y / (horizontalRayCount - 1);
verticalRaySpacing = bounds.size.y / (verticalRayCount - 1);
}
struct RaycastOrigins {
public Vector2 topLeft, topRight;
public Vector2 bottomLeft, bottomRight;
}
}
Edit: The code is (as I understand from a half an hour tutorial.) just defining attributes to a quad(player) so that it can collide with another quad(obstacle). at the moment the code should only work vertically. so I am testing it by drawing the obstacle under the player and pressing play. the player should come to rest on the obstacle but instead, it sinks straight through it.
My screen after selecting the player block
and asking about debugging, I dont understand the language well enough to not break anything while trying to debug it.
I just got someone on discord to help and it turns out I didn't have the obstacle set with boxcollider2D
Many Thanks all who tried.

Combine camera pan to with touch controls in Unity

I've been asked to look into creating a simple iterative application with Unity. This application has 2 major functions regarding the camera.
LERP-ing the camera to focus on a target object.
Once moved relinquish control to the user and allow the user to rotate and zoom around the object.
I'm new to this but I've managed to create two scripts that achieve these goals in isolation. Now I'm struggling to fit them together.
I'll start with the relevant code for user interaction.
First, I use TouchKit to set the delta values on each frame this is in Start.
// set the delta on each frame for horizontal and vertical rotation
var oneTouch = new TKPanRecognizer();
oneTouch.gestureRecognizedEvent += (r) =>
{
HorizontalDelta += r.deltaTranslation.x * rotateSpeed * Time.deltaTime;
VerticalDelta -= r.deltaTranslation.y * rotateSpeed * Time.deltaTime;
};
// do the same for pinch
var pinch = new TKPinchRecognizer();
pinch.gestureRecognizedEvent += (r) =>
{
rotateDistance -= r.deltaScale * 200.0f * Time.deltaTime;
};
TouchKit.addGestureRecognizer(oneTouch);
TouchKit.addGestureRecognizer(pinch);
And on Update:
VerticalDelta = Mathf.Clamp(VerticalDelta, verticalPivotMin, verticalPivotMax);
var direction = GetDirection(HorizontalDelta, VerticalDelta);
var currentTarget = targetsSwitched ? target2 : target;
transform.position = currentTarget.position - direction * rotateDistance;
transform.LookAt(currentTarget.position);
// ...
private Vector3 GetDirection(float x, float y)
{
Quaternion q = Quaternion.Euler(y, x, 0);
return q * Vector3.forward;
}
This works beautifully and does exactly what I want. The problem comes when I try to integrate this code with my camera moving script. This shows where I want to add the Update code
void Update ()
{
if (currentlyMoving)
{
FocusTarget(currentTarget);
}
else
{
// accept user input if not moving
if (Input.GetKeyDown(KeyCode.Space))
{
SetMoveToTarget(mainTargetObject);
}
if (Input.GetKeyDown(KeyCode.Q))
{
SetMoveToTarget(subTargetObject1);
}
if (Input.GetKeyDown(KeyCode.E))
{
SetMoveToTarget(subTargetObject2);
}
}
}
These are the functions that actually move the camera:
void SetMoveToTarget(GameObject target)
{
if (currentlyMoving == false)
{
currentlyMoving = true;
fromRotation = currentTarget.transform.rotation;
currentTarget = target;
toRotation = currentTarget.transform.rotation;
timeStartedLerping = Time.time;
}
}
void FocusTarget(GameObject target)
{
float timeSinceStarted = Time.time - timeStartedLerping;
float percentageComplete = timeSinceStarted / (lerpSpeed);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, moveSpeed * Time.deltaTime);
transform.rotation = Quaternion.Lerp(fromRotation, toRotation, Mathf.Pow(percentageComplete, (float)1.2));
if (Vector3.Distance(transform.position, target.transform.position) < 0.1 && percentageComplete > 0.99)
{
transform.position = target.transform.position;
transform.rotation = target.transform.rotation;
currentlyMoving = false;
}
}
I think what I need to do (and I may be wrong on this) is set rotateDistance to be the difference between the currentTarget in the second script and currentTarget in the first script.
Thank you in advance, it's quite a tricky one for me.
In the end I had to change how I was dealing with moving the camera in the first place. The problem was that moving to a set game object worked and was easy to set-up but it's much easier to integrate with the look script if you actually calculate when the next position of the camera should be and move to that.
I'm going to paste the working product here for posterity, it's missing some stuff from the end game but the camera is working.
using UnityEngine;
using UnityEngine.UI;
public class NewCamera : MonoBehaviour {
// targets
public GameObject target;
public GameObject target2;
// settings
public float RotateSpeed = 50.0f;
public float RotateDistance = 3;
public float CameraMoveSpeed = 20f;
public float VerticalPivotMin = 5;
public float VerticalPivotMax = 65;
public float MinZoomIn = 1.7f;
public float MaxZoomIn = 4f;
// private
private GameObject lookTarget;
private bool currentlyMoving = false;
private Vector3 targetVector;
private float timeStartedLerping;
private float HorizontalDelta;
private float VerticalDelta;
void Start ()
{
lookTarget = target;
var oneTouch = new TKPanRecognizer();
oneTouch.gestureRecognizedEvent += (r) =>
{
if (currentlyMoving == false)
{
HorizontalDelta += r.deltaTranslation.x * RotateSpeed * Time.deltaTime;
VerticalDelta -= r.deltaTranslation.y * RotateSpeed * Time.deltaTime;
VerticalDelta = Mathf.Clamp(VerticalDelta, VerticalPivotMin, VerticalPivotMax);
}
};
var pinch = new TKPinchRecognizer();
pinch.gestureRecognizedEvent += (r) =>
{
if (currentlyMoving == false)
{
RotateDistance -= r.deltaScale * 200.0f * Time.deltaTime;
RotateDistance = Mathf.Clamp(RotateDistance, MinZoomIn, MaxZoomIn);
}
};
TouchKit.addGestureRecognizer(oneTouch);
TouchKit.addGestureRecognizer(pinch);
}
void Update ()
{
if (currentlyMoving)
{
FocusTarget();
}
else
{
var direction = GetDirection(HorizontalDelta, VerticalDelta);
transform.position = lookTarget.transform.position - direction * RotateDistance;
transform.LookAt(lookTarget.transform.position);
}
}
public void OnButtonClick()
{
var currentTarget = target.GetInstanceID() == lookTarget.GetInstanceID() ? target : target2;
var nextTarget = currentTarget.GetInstanceID() == target.GetInstanceID() ? target2 : target;
var cameraPosition = transform.position;
var targetPosition = currentTarget.transform.position;
SetMoveToTarget(nextTarget, cameraPosition - targetPosition);
}
void SetMoveToTarget(GameObject moveTo, Vector3 offset)
{
currentlyMoving = true;
targetVector = moveTo.transform.position + offset;
lookTarget = moveTo;
}
void FocusTarget()
{
transform.position = Vector3.Lerp(transform.position, targetVector, CameraMoveSpeed * Time.deltaTime);
if (Vector3.Distance(transform.position, targetVector) < 0.01)
{
transform.position = targetVector;
currentlyMoving = false;
}
}
private Vector3 GetDirection(float x, float y)
{
Quaternion q = Quaternion.Euler(y, x, 0);
return q * Vector3.forward;
}
}
If you want to use it for any reason, just add this script to a camera and set the two targets. I have to make a more robust version where you can add new targets easily but this will do for now.
Happy hacking!

Categories