I am trying to make a movement where sprites go down in a row inside a panel, and then they stop when reaching the bottom boundry of the panel. Problem is after i set the position of the objects where it has to stop moving, its taking me position.y = 0 as if it was the middle of the screen. How do i set it to stop moving, when reaching the bottom of its parent object (the panel). Thank you.
here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Transform_test : MonoBehaviour
{
[SerializeField]
SpriteRenderer[] Reels;
[SerializeField]
Transform[] ReelsTransform;
int speed;
bool drop;
[SerializeField]
GameObject Panel;
void Start()
{
Debug.Log(transform);
//transform.position = new Vector3 (10,0,0);
speed = 1;
drop = false;
}
// Update is called once per frame
void Update()
{
// test();
if (drop == false)
{
foreach (Transform Reels in ReelsTransform)//This Targets All Children Objects Of The Main Parent Object
{
Debug.Log("pos " + Reels.transform.position.y);
for (int i = 0;i<5;i++)
{
//Direction And Speed Of Movement
new WaitForSeconds(Random.Range(1, 3));
Reels.transform.Translate(Vector3.down * Time.smoothDeltaTime * speed, Space.World);
if (Reels.transform.position.y <= 0 )
{
Reels.transform.position = new Vector3(Reels.transform.position.x, Reels.transform.position.y + 58, Reels.transform.position.z);
}
}
}
}
}
}
Ok this is how i solved it. I created an empty sprite on the place where i want the reel to stop, then i set the if to match that position with the position of the first sprite in the reel. If the positions match. Turn moveto to true and stop the movement
public class transform2 : MonoBehaviour
{
public float speed;
public SpriteRenderer sprite1;
public SpriteRenderer[] sprites;
public SpriteRenderer cell1;
public GameObject Panel;
bool moveto;
void move()
{
if (moveto == false)
{
foreach (SpriteRenderer sprites in sprites)
{
sprites.transform.Translate(Vector3.down * Time.smoothDeltaTime * speed, Space.World);
if (sprite1.transform.position.y <= cell1.transform.position.y)
{
moveto = true;
sprite1.transform.position = cell1.transform.position;
}
Debug.Log("MOVE");
}
}
}
// Start is called before the first frame update
void Start()
{
speed = 20;
}
// Update is called once per frame
void Update()
{
move();
}
}
Related
Context
So I'm making a clone of Crossy Roads where the camera follows the player. It does some linear interpolation (Lerp) after moving, and the camera starts moving away from the player in the positive direction (x-axis until camera reaches to a certain range where player is not visible enough). Things I have tried is by flagging it, but I think I'm doing it wrong.
Problem
I have done my camera movements accordingly, but I am having an issue where the conditions are not properly met. I'm get the offset camera after not moving, but it does not the Lerp, and vice-versa. I want both to happen after a certain condition after the game starts. When the player moves, the camera follows it in Lerp. However, once the player is "Idle", its still Lerping. I want the camera to continue by itself and at the same time focus at the player's object.
Example
Camera with Lerp, but not moving away from the player
Camera moving away, but not following player with lerp
Code
CameraController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
** Camera following player (Smoothing and angle): https://youtu.be/4HpC--2iowE
** Maximo: https://www.youtube.com/watch?v=bXNFxQpp2qk&ab_channel=iHeartGameDev
** Moving character relative to camera: https://forum.unity.com/threads/moving-character-relative-to-camera.383086/
** Camera follow v2: https://youtu.be/Jpqt2gRHXtc?list=PLq_nO-RwB516fNlRBce0GbtJSfysAjOgU
*/
public class CameraController : MonoBehaviour
{
public GameObject player;
public PlayerControl playerControlScript;
private Vector3 newCameraPos;
public bool stillIdle;
void Start()
{
stillIdle = false;
PlayerControl playerControlScript = GetComponent<PlayerControl>();
}
void LateUpdate()
{
player = GameObject.FindGameObjectWithTag("Player");
if (playerControlScript.GetfirstInput()) //True
{
stillIdle = true;
newCameraPos = Vector3.Lerp(transform.position, playerControlScript.transform.position, Time.deltaTime);
transform.position = new Vector3(newCameraPos.x, 1, newCameraPos.z);
}
if (stillIdle)
transform.position = new Vector3(transform.position.x + 0.69f * Time.deltaTime, transform.position.y, transform.position.z); //Moving camera away effect
}
}
PlayerControl.cs
public class PlayerControl : MonoBehaviour
{
bool firstInput;
Vector3 startPos;
Vector3 endPos;
public bool GetfirstInput() //I was learning how to have a Get function while my member was private from another script file
{
return firstInput;
}
void Update()
{
if (Input.GetButtonDown("up") || Input.GetButtonDown("left") || Input.GetButtonDown("right") || Input.GetButtonDown("down"))
{
//if game starts
{
//Other variables being initialized here
firstInput = true;
}
}
}
}
Hierarchy/Inspector
Main Camera
Player Object
Some help would be appreciate it. I feel I have been staring at this problem and I bet it is something minimal and small from just thinking it.
Let me know if you need clarifications. I'm happy to edit and answer them for everyone
If player in your game does not change, you don't have to find the player reference in each LateUpdate().
I notice that once stillIdle is set true, it never goes back to false, is this your intention to do that?
You call Lerp when playerControlScript.GetfirstInput() is true, so maybe we need to look at its implementation. Maybe it turns true in some conditions you do not intend it to.
Maybe Try this
public class PlayerControl : MonoBehaviour
{
private bool _hasFireFirstInput = false;
private bool _isIdle = true;
public bool IsIdle => _isIdle;
public bool HasFireFirstInput => _hasFireFirstInput;
private void Update()
{
if (Input.GetButton("Horizontal") || Input.GetButton("Vertical"))
{
_hasFireFirstInput = true;
_isIdle = false;
Vector3 pos = transform.position;
pos = new Vector3(pos.x, pos.y, pos.z + .2f * Time.deltaTime);
transform.position = pos;
}
else
{
_isIdle = true;
}
}
}
I use Input.GetButton() rather than Input.GetButtonDown(), since the later only return true at the frame the button is pressed, meaning that if i long-press the button, it will return false after the next frame.
public class CameraController : MonoBehaviour
{
[SerializeField] PlayerControl _playerControlScript;
[SerializeField] Vector3 _offset;
[SerializeField] float _lerpSpeed;
bool _iskeepLerping = false;
float _lerpVal = 0f;
private void LateUpdate()
{
if (!_playerControlScript.HasFireFirstInput)
{
return;
}
if (_playerControlScript.IsIdle)
{
MoveAway();
}
else
{
Lerp();
}
}
private void MoveAway()
{
_iskeepLerping = false;
transform.position = new Vector3(transform.position.x + 0.69f * Time.deltaTime, transform.position.y, transform.position.z);
}
private void Lerp()
{
if (!_iskeepLerping)
{
_lerpVal = 0f;
}
Vector3 newCameraPos = Vector3.Lerp(transform.position, _playerControlScript.transform.position + _offset, _lerpVal);
transform.position = newCameraPos;
_lerpVal += _lerpSpeed * Time.deltaTime;
_iskeepLerping = true;
}
}
I'm making a 2D game and i have spawning potion items in a making potion scene first of all i want to make the item pop up after spown and go like in this video that i recorded from my game:
Spawned Item do not move like the original
what do i do to make the spawned (Cloned) Item move the same as the original potion item?
Secondly, i want to spawn random and more than one item (potion) each time the scene starts how do i do that knowing that i'm using this script that spawns only one object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HerbSpawner : MonoBehaviour
{
//Here, we declare variables.
public GameObject objToSpawn;
public Transform groupTransform;
//public means the var is exposed in the inspector, which is super helpful.
// Start is called before the first frame update
Vector2 spawnPos;
void Start()
{
spawnPos = Camera.main.ViewportToWorldPoint(new Vector2(0.5f, 0.5f));
}
// Update is called once per frame
void Update()
{
//let's also spawn on button press:
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.GetRayIntersection(Camera.main.ScreenPointToRay(Input.mousePosition));
if (hit.collider && hit.collider.CompareTag("Bush"))
{
SpawnIt();
}
}
void SpawnIt()
{
Vector2 spawnPos = Camera.main.ViewportToWorldPoint(new Vector2(0.5f, 0.7f));
Instantiate(objToSpawn, spawnPos, Quaternion.identity, groupTransform);
}
}
}
please let me know if there is anyway to do it spawn multiple objects randomly and make the movement for the items to popup like in the video. This is teh script i used for that:
using System.Collections;
using UnityEngine;
using TMPro;
public class DragNDropItem : MonoBehaviour
{
public string itemName;
private bool dragging;
private Vector2 firstPos;
private Color spriteRenderer;
[SerializeField] private float speed = 3;
[SerializeField] private GameObject boiler;
public TMP_Text itemNameText;
private BoilerScript boilerScript;
public AudioSource drop;
void Awake()
{
// Initial position of the item
firstPos = transform.position;
boiler = GameObject.FindGameObjectWithTag("Boiler");
boilerScript = boiler.GetComponent<BoilerScript>();
spriteRenderer = gameObject.GetComponent<SpriteRenderer>().color;
}
void OnMouseDown()
{
dragging = true;
}
void OnMouseUp()
{
dragging = false;
if (Vector2.Distance(transform.position, boiler.transform.position) < 0.7f) // We control the distance between the item and the cauldron without using the collider.
{
itemNameText.text = itemName;
boilerScript.Potion(); // Checks the recipe's completion status each time an item is placed.
spriteRenderer.a = 0f;
gameObject.GetComponent<SpriteRenderer>().color = spriteRenderer;
StartCoroutine(Alpha());
}
else drop.Play(); // If the item is left
}
void Update()
{
if (dragging) // As soon as the item is clicked with the mouse, the item follows the mouse.
{
Vector2 mousePosition = MousePos();
transform.position = Vector2.Lerp(transform.position, mousePosition, speed * Time.deltaTime);
} else if (!dragging && (Vector2)transform.position != firstPos) // As soon as we stop clicking, it takes it back to its original place. While the item is in its original location, we are constantly preventing the code from running.
{
transform.position = Vector2.Lerp(transform.position, firstPos, speed * Time.deltaTime);
}
}
Vector2 MousePos() // position of mouse
{
return Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
private IEnumerator Alpha() // After a second the item becomes visible.
{
spriteRenderer.a = 1f;
yield return new WaitForSeconds(0.6f);
gameObject.GetComponent<SpriteRenderer>().color = spriteRenderer;
}
}
Your colliders aren't set to trigger. My guess is, when a clone is spawned, it collides with the existing potion object.
Hey Guys I've been having a problem lately that I cant seem to solve.
A sprite is supposed to roam around (as it does) while nothing is inside its radius, however if the player moves close to it the sprite should theoretically move towards it and stop roaming.
The sprite doesn't follow the player and cant even see its tag since I cant even see the contents of the "Collider2D[] hits".
using System.Collections.Generic;
using UnityEngine;
public class FireCultist : MonoBehaviour
{
public float moveTimeSeconds; //Time it will take object to move, in seconds.
private float xMax = 10.0f; // The boundaries of the spawn area
private float yMax = 10.0f;
private float xMin = -10.0f; // The boundaries of the spawn area
private float yMin = -10.0f;
public int xDistanceRange; // The max distance you can move at one time
public int yDistanceRange;
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
public Vector2 start;
public Vector2 end;
public bool roam = true;
public Collider2D[] hits;
void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
rb2D = GetComponent<Rigidbody2D>();
inverseMoveTime = 1f / moveTimeSeconds;
InvokeRepeating("RandomMove", 0.0f, 5.0f);
}
void Update()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 10); // returns all colliders within radius of enemy
int i = 0;
while(hits.Length > i)
{
Debug.Log("Sees");
Debug.Log(hits[i]);
i++;
}
followPlayer();
if (roam)
{
Debug.Log("Roam");
transform.position = Vector2.MoveTowards(transform.position, end, inverseMoveTime * Time.deltaTime); //Random move to new position
}
}
public void followPlayer()
{
foreach (Collider2D hit in hits)
{
if (hit.tag == "Player") // if the player is within a radius
{
Debug.Log("Chasing Player");
transform.position = Vector2.MoveTowards(transform.position, GameObject.Find("Prefabs/Player").GetComponent<Transform>().position, inverseMoveTime * Time.deltaTime); // chase player
roam = false;
}
else
{
Debug.Log("Continues");
roam = true; // Continue RandomMove()
}
}
}
public void RandomMove() // gets random coordinates near enemy and moves there
{
float xDistance = Random.Range(-xDistanceRange, xDistanceRange); // check
float yDistance = Random.Range(-yDistanceRange, yDistanceRange);// check
if (xDistance < xMax && xDistance > xMin && yDistance < yMax && yDistance > yMin && roam == true) // If within boundaries of walking space
{
start = transform.position;
end = start + new Vector2(xDistance, yDistance);
Debug.Log("Roaming From : " + start + " To : " + end);
}
}
}
The roaming algorithm works however not too sure about the tag detection.
The script belongs to this enemy object
Player Object Properties
It looks like you are declaring a new hits variable during every update instead of using your class-level variable. This means the variable inside of followPlayer() will never be instantiated, and the information will not be passed between the two methods.
Try this:
void Update()
{
hits = Physics2D.OverlapCircleAll(transform.position, 10);
//...
}
You might also consider attaching a trigger collider to the enemy which sends a notification when the player enters/exits the trigger instead of relying on OverlapCircleAll()
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = false;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = true;
}
}
So i made a little side scroller that uses a prefab to spawn bullets.
My problem is that it only shoots to one side... the Right.
I need it to fire to the left as well. I've already made a variable to see if the player is looking to the right or left.
I've tried to put the Speed to -20 and i've tried to rotate it 180 degrees on it's Z axis. I tested if the bullet script even picked up the change from the player movement script and it does.
Player Movement script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public GameObject bullet;
private Rigidbody2D myRigidbody;
private float speed = 15;
private bool facingRight;
private bool ground = false;
private float jump = 23;
// Start is called before the first frame update
void Start()
{
facingRight = true;
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate()
{
float horizontal = Input.GetAxis("Horizontal");
Movement(horizontal);
Flip(horizontal);
if (Input.GetKey("w"))
{
if (ground)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, jump);
}
}
if(facingRight == false)
{
bullet.GetComponent<bullet>().left = true;
}
if (facingRight == true)
{
bullet.GetComponent<bullet>().left = false;
}
}
void OnTriggerEnter2D()
{
ground = true;
}
void OnTriggerExit2D()
{
ground = false;
}
private void Movement(float horizontal)
{
myRigidbody.velocity = new Vector2(horizontal * speed,myRigidbody.velocity.y);
}
private void Flip(float horizontal)
{
if (horizontal > 0 && !facingRight || horizontal < 0 && facingRight)
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
}
Weapon script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class weapon : MonoBehaviour
{
// Start is called before the first frame update
public bool right;
public Transform firepointR;
public GameObject bulletPrefab;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("space"))
{
Debug.Log("Oh well");
Shoot();
}
}
void Shoot()
{
Instantiate(bulletPrefab, firepointR.position, firepointR.rotation);
}
}
bullet script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class bullet : MonoBehaviour
{
public bool left;
public float speed = 20;
public Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb.velocity = transform.right * speed;
}
// Update is called once per frame
void FixedUpdate()
{
Debug.Log(speed);
if (left == false)
{
transform.Rotate(0, 0, 180);
}
}
}
As i previously said i need my bullet (prefab) to go the opposite direction but whatever i do right now it will always go right.
Expanding on my comment:
Did you try reversing the velocity?
rb.velocity = -(transform.right * speed);
Note: There are instances when using a negative float wont return the opposite of the positive result. However, in this example, it will work just fine.
I imagine this will work also (and is probably the correct way of doing it):
rb.velocity = transform.left * speed;
When the game starts, a random waypoint is selected from an array. The camera should then rotate to face the selected random waypoint and start moving towards it.
Once the camera has reached the waypoint, it should wait 3 seconds before rotating to face and move towards the next random waypoint.
The problem I have is in Start(). The camera does not rotate to face the first waypoint before it starts moving towards it. Instead, it moves towards the first waypoint backwards. Then, when it reaches the waypoint, it waits 3 seconds to rotate and move towards the next waypoint.
It's working fine except that the camera does not rotate to face the first selected random waypoint. It's moving to it backward without first rotating to face it.
Here's my code:
The waypoints script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public GameObject[] waypoints;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private int current = 0;
private bool rot = false;
public void Init()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
if(waypoints.Length > 0)
{
StartCoroutine(RotateFacingTarget(waypoints[UnityEngine.Random.Range(0, waypoints.Length)].transform));
}
}
void Update()
{
if (waypoints.Length > 0)
{
if (Vector3.Distance(waypoints[current].transform.position, transform.position) < WPradius)
{
current = UnityEngine.Random.Range(0, waypoints.Length);
rot = false;
StartCoroutine(RotateFacingTarget(waypoints[current].transform));
if (current >= waypoints.Length)
{
current = 0;
}
}
if (rot)
transform.position = Vector3.MoveTowards(transform.position, waypoints[current].transform.position, Time.deltaTime * speed);
}
}
IEnumerator RotateFacingTarget(Transform target)
{
yield return new WaitForSeconds(3);
lookAtCam.target = target;
rot = true;
}
}
The look at camera script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LookAtCamera : MonoBehaviour
{
//values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
//values for internal use
private Quaternion _lookRotation;
private Vector3 _direction;
// Update is called once per frame
void Update()
{
//find the vector pointing from our position to the target
if (target)
{
_direction = (target.position - transform.position).normalized;
//create the rotation we need to be in to look at the target
_lookRotation = Quaternion.LookRotation(_direction);
//rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime * RotationSpeed);
}
}
}
How can I fix this?
Let's assume that Waypoints.Init() is being called and your waypoints variable has an array of 3.
Waypoints.Init() starts a coroutine
Your coroutine waits 3 seconds
After 3 seconds, you set your camera target which Slerps to face the position
Update on its first frame says waypoints.Length > 0 == true
It's not close to its target, and rot is false, so it does not move
Now, you're waiting 3 seconds, not rotating, and not moving.
Your coroutine's 3 second wait time is up and starts the rotation toward your target
rot is now true at the start of your rotation, so your Update method starts moving toward the target as well
It would seem that your logic is off in how the order of operations works. If it needs to act as you describe, I suggest that you operate on the target differently.
I've implemented the following using an enum:
Waypoints
public class Waypoints : MonoBehaviour
{
private GameObject[] waypoints;
private Transform currentWaypoint;
private enum CameraState
{
StartRotating,
Rotating,
Moving,
Waiting
}
private CameraState cameraState;
public GameObject player;
public float speed = 5;
public float WPradius = 1;
public LookAtCamera lookAtCam;
private int current = 0;
private bool isCameraRotating = false;
void Start()
{
cameraState = CameraState.StartRotating;
}
void Update()
{
switch (cameraState)
{
// This state is used as a trigger to set the camera target and start rotation
case CameraState.StartRotating:
{
// Sanity check in case the waypoint array was set to length == 0 between states
if (waypoints.Length == 0)
break;
// Tell the camera to start rotating
currentWaypoint = waypoints[UnityEngine.Random.Range(0, waypoints.Length)].transform;
lookAtCam.target = currentWaypoint;
cameraState = CameraState.Rotating;
break;
}
// This state only needs to detect when the camera has completed rotation to start movement
case CameraState.Rotating:
{
if (lookAtCam.IsFinishedRotating)
cameraState = CameraState.StartMoving;
break;
}
case CameraState.Moving:
{
// Move
transform.position = Vector3.MoveTowards(transform.position, currentWaypoint.position, Time.deltaTime * speed);
// Check for the Waiting state
if (Vector3.Distance(currentWaypoint.position, transform.position) < WPradius)
{
// Set to waiting state
cameraState = CameraState.Waiting;
// Call the coroutine to wait once and not in CameraState.Waiting
// Coroutine will set the next state
StartCoroutine(WaitForTimer(3));
}
break;
}
case CameraState.Waiting:
// Do nothing. Timer has already started
break;
}
}
IEnumerator WaitForTimer(float timer)
{
yield return new WaitForSeconds(timer);
cameraState = CameraState.StartRotating;
}
public void RefreshWaypoints()
{
waypoints = GameObject.FindGameObjectsWithTag("Target");
}
}
LookAtCamera
public class LookAtCamera : MonoBehaviour
{
// Values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
private float timer = 0.0f;
public bool IsRotationFinished
{
get { return timer > 0.99f; }
}
// Update is called once per frame
void Update()
{
if (target != null && timer < 0.99f)
{
// Rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation((target.position - transform.position).normalized),
timer);
timer += Time.deltaTime * RotationSpeed;
}
}
}