I've been trying a new method I found about camera movement (Im trying to do like a metroidvania) and i just made a script that makes the camera size a collider so the camera can only move across it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
private Transform player;
private BoxCollider2D camBox;
private GameObject[] boundaries;
private Bounds[] allBounds;
private Bounds targetBounds;
public float speed;
private float waitForSeconds = 0.5f;
void start()
{
player = GameObject.Find("Player").GetComponent<Transform>();
camBox = GetComponent<BoxCollider2D>();
FindLimits();
}
void LateUpdate()
{
if(waitForSeconds > 0)
{
waitForSeconds -= Time.deltaTime;
}else{
SetOneLimit();
FollowPlayer();
}
}
//Encuentra el encaje de la camara
void FindLimits()
{
boundaries = GameObject.FindGameObjectsWithTag("Boundary");
allBounds = new Bounds[boundaries.Length];
for(int i = 0; i < allBounds.Length; i++)
{
allBounds[i] = boundaries[i].gameObject.GetComponent<BoxCollider2D>().bounds;
}
}
//Ajusta la medida de la camara al borde
void SetOneLimit()
{
for(int i = 0; i < allBounds.Length; i++)
{
if(player.position.x > allBounds[i].min.x && player.position.x < allBounds[i].max.x && player.position.y > allBounds[i].min.y && player.position.y < allBounds[i].max.y)
{
targetBounds = allBounds[i];
return;
}
}
}
void FollowPlayer()
{
float xTarget=camBox.size.x<targetBounds.size.x?Mathf.Clamp(player.position.x, targetBounds.min.x+camBox.size.x/2, targetBounds.max.x-camBox.size.x/2):(targetBounds.min.x+targetBounds.max.x)/2;
float yTarget=camBox.size.y<targetBounds.size.y?Mathf.Clamp(player.position.y, targetBounds.min.y+camBox.size.y/2, targetBounds.max.y-camBox.size.y/2):(targetBounds.min.y+targetBounds.max.y)/2;
Vector3 target = new Vector3(xTarget, yTarget, transform.position.z);
transform.position = Vector3.Lerp(transform.position, target, speed * Time.deltaTime);
}
}
This is the code that im using and this is the error that I get when pressing the play button.
NullReferenceException: Object reference not set to an instance of an object
CameraFollow.SetOneLimit () (at Assets/Scripts/CameraFollow.cs:46)
CameraFollow.LateUpdate () (at Assets/Scripts/CameraFollow.cs:28)
start() is written lower case, I'm not sure it will be work same as Start() ;)
Check it because probably your field aren't initialized propertly.
And read some about NullReferenceException ;)
Related
How to make Local Space TrailRenderer
I want to make TrailRenderer use Local Space. Because I use TrailRenderer in my Player's Sword Effect like that Image.
When the Player stopped in place, there is no problem. But as the player moves, the TrailRender moves too. So the TrailRenderer'shape is strange like that Image.
I tried all of TrailRenderer's Z Position set 0 But that's failed.
How to solve this Problem??
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Trail : MonoBehaviour
{
[SerializeField]
private Transform _trailPos;
TrailRenderer _renderer;
[SerializeField]
private Vector3[] _t;
private void Start()
{
_renderer = GetComponent<TrailRenderer>();
}
private void LateUpdate()
{
transform.position = _trailPos.position;
for (int i = 0; i < _renderer.positionCount; ++i)
{
Vector3[] v = new Vector3[_renderer.positionCount];
_renderer.GetPositions(v);
for (int j = 0; j < v.Length; ++j)
{
v[j] = (Vector2)v[j];
}
_renderer.SetPositions(v);
}
_t = new Vector3[_renderer.positionCount];
_renderer.GetPositions(_t);
}
}
You could probably try and track your player movement and apply the same delta to the trail positions.
Something like e.g.
[SerializeField] private TrailRenderer trailRenderer;
[SerializeField] private Transform player;
private Vector3 lastFramePosition;
private void Start()
{
lastFramePosition = player.position;
}
private void LateUpdate()
{
var delta = player.position - lastFramePosition;
lastFramePosition = player.position;
var positions = new Vector3[trailRenderer.positionCount];
trailRenderer.GetPositions(positions);
for (var i = 0; i < trailRenderer.positionCount; i++)
{
positions[i] += delta;
}
trailRenderer.SetPositions(positions);
}
So I've been making a small game to do with Snake, and I'm struggling as to how I can make the Snake's Body appear and follow the position of the head whenever the snake's head touches the apple. I've managed to get the Snake's body to spawn but I can't get it to follow the head correctly. Can anyone help me and tell me how I can do this? Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Snake_Move : MonoBehaviour
{
// variables
public Vector2 pos;
private Vector2 moveDirection;
private float moveTimer;
private float timerSeconds;
//Function which runs once when the program starts
void Start()
{
timerSeconds = 0.0167f;
moveTimer = timerSeconds;
moveDirection = new Vector2(0.1f, 0);
}
//Function which updates itself based on your refresh rate
public void Update()
{
AutoMove();
ChangeDirection();
transform.position = new Vector2(pos.x, pos.y);
transform.eulerAngles = new Vector3(0, 0, AngleCalculator(moveDirection) - 90);
}
//Moves the snake 60 units each second
private void AutoMove()
{
moveTimer += Time.deltaTime;
if (moveTimer > timerSeconds)
{
pos += moveDirection;
moveTimer -= timerSeconds;
}
}
//Changes direction of the snake based on arrow key pressed
private void ChangeDirection()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (moveDirection.y != -0.1f)
{
moveDirection.x = 0;
moveDirection.y = 0.1f;
}
}
else if (Input.GetKeyDown(KeyCode.DownArrow))
{
if (moveDirection.y != 0.1f)
{
moveDirection.x = 0;
moveDirection.y = -0.1f;
}
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
if (moveDirection.x != -0.1f)
{
moveDirection.y = 0;
moveDirection.x = 0.1f;
}
}
else if (Input.GetKeyDown(KeyCode.LeftArrow))
{
if (moveDirection.x != 0.1f)
{
moveDirection.y = 0;
moveDirection.x = -0.1f;
}
}
}
//Calculates the angle at which the snake is moving; used to calculate rotation of sprite
private float AngleCalculator(Vector2 direction)
{
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
return angle;
}
private void SnakeBodySprite()
{
GameObject snakeApple = GameObject.Find("SnakeApple");
Apple_RandomSpawn appleScript = snakeApple.GetComponent<Apple_RandomSpawn>();
//something here?????
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Apple_RandomSpawn : MonoBehaviour
{
private Vector2 foodPos;
void Start()
{
SpawnApple();
}
void Update()
{
transform.position = new Vector2(foodPos.x, foodPos.y);
SnakeAte();
}
public void SpawnApple()
{
foodPos = new Vector2(Random.Range(-17, 17), Random.Range(-9, 9));
}
public void SnakeAte()
{
GameObject snakeBody = GameObject.Find("SnakeBody");
GameObject snakeHead = GameObject.Find("SnakeHead");
Snake_Move snakeMove = snakeHead.GetComponent<Snake_Move>();
if (foodPos.x <= snakeMove.pos.x + 1 &&
foodPos.x >= snakeMove.pos.x - 1 &&
foodPos.y <= snakeMove.pos.y + 1 &&
foodPos.y >= snakeMove.pos.y -1)
{
SpawnApple();
Instantiate(snakeBody);
}
}
}
This is kind of a broad question, so it's hard to answer completely. The thought that comes to my mind is using a Queue of move Vectors for the head, and then applying those movements to the body. You would need a reference to the snake body object, and need to know how many movements the head is from the body. It looks like that might be 60 in your case.
I would add the Queue in this section:
Queue<Vector2> moveDirections = new Queue<Vector2>();
SnakeBody snakeBody; // Reference to 1st snake body element
float offset = 60; // ?
private void AutoMove()
{
moveTimer += Time.deltaTime;
// enqueue the most recent moveDirection
moveDirections.Enqueue(moveDirection);
if (moveTimer > timerSeconds)
{
pos += moveDirection;
moveTimer -= timerSeconds;
// Get oldest moveDirection from the head to apply to the body
Vector2 bodyMovement = moveDirections.Dequeue();
snakeBody.transform.Translate(bodyMovement);
}
// If the queue count is greater than how far ahead the head should be from the
// first body element, remove one from queue.
if (moveDirections.Count > offset) {
moveDirections.Dequeue();
}
}
This would only work for the first body element, so you could add a script to each body element keeping track of its child body element. Position the child element with the same logic used above.
NOTE: I haven't tested this, I'm just trying to give a high-level overview of how to solve this problem.
So, when I start the game, my character can jump on the first platform (because that is the manually placed platform), but I cannot jump on the spawned floors. BTW I am able to run on the floors and I know my jump works correctly.
I have tried so many ways of collider detection I am going crazy and I know its a simple fix that I just can't figure out.
I expected my character to be able to jump on the duplicated platforms but the character just doesn't do anything at all.
If anyone is willing to take a look that would be very helpful. - Nick
P.S I know my code is messy.
CODE:
#Code that is on my player script#
using System;
using System.Diagnostics;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using TouchControlsKit;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Text;
using System.IO;
public class Attack : MonoBehaviour
{
const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded
[SerializeField] private LayerMask m_WhatIsGround;
[SerializeField] private Transform m_GroundCheck;
private bool m_Grounded;
public Collider2D objectCollider;
public Collider2D anotherCollider;
[Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f;
private Timer t;
private Timer a;
private float timeStamp;
private float die = 0;
public GameObject bullet;
private bool m_FacingRight = true;
public float move;
private Vector3 velocity = Vector3.zero;
public GameObject idle_0;
public playscript play;
public Transform player;
private Rigidbody2D m_Rigidbody2D;
[SerializeField] private float m_JumpForce = 200f;
bool swing = false;
bool isgrounded = false;
public bool canJump = false;
bool slide = false;
public Transform groundLayer; // Insert the layer here.
public Vector2 jumpHeight;
private Vector2 touchOrigin = -Vector2.one;
public Vector2 moveSpeed;
public bool run;
Collider2D m_Collider;
// variable to hold a reference to our SpriteRenderer component
private SpriteRenderer mySpriteRenderer;
// This function is called just one time by Unity the moment the component loads
private void Awake()
{
// get a reference to the SpriteRenderer component on this gameObject
mySpriteRenderer = GetComponent<SpriteRenderer>();
animator.SetBool("death", false);
}
public Animator animator;
Animator anim;
int swingHash = Animator.StringToHash("swing");
// Use this for initialization
void Start()
{
timeStamp = Time.time + 5;
m_Collider = GetComponent<Collider2D>();
run = false;
m_Rigidbody2D = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
animator.SetBool("isgrounded", false);
isgrounded = false;
canJump = false;
animator.SetBool("swing", false);
}
private void FixedUpdate()
{
m_Grounded = false;
// The player is grounded if a circlecast to the groundcheck position hits anything designated as ground
// This can be done using layers instead but Sample Assets will not overwrite your project settings.
Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround);
for (int i = 0; i < colliders.Length; i++)
{
if (colliders[i].gameObject != gameObject)
animator.SetBool("isgrounded", true);
m_Grounded = true;
}
}
// Update is called once per frame
void Update()
{
anotherCollider = GameObject.FindGameObjectWithTag("Ground").GetComponent<BoxCollider2D>();
objectCollider = GameObject.FindGameObjectWithTag("Player").GetComponent<CapsuleCollider2D>();
Vector3 targetVelocity = new Vector2(move * 2f, m_Rigidbody2D.velocity.y);
m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref velocity, m_MovementSmoothing);
animator.SetBool("run", true);
if (move > 0 && !m_FacingRight)
{
// ... flip the player.
Flip();
}
// Otherwise if the input is moving the player left and the player is facing right...
else if (move < 0 && m_FacingRight)
{
// ... flip the player.
Flip();
}
int horizontal = 0; //Used to store the horizontal move direction.
int vertical = 0; //Used to store the vertical move direction.
#if UNITY_STANDALONE || UNITY_WEBPLAYER
//Check if we are running on iOS, Android, Windows Phone 8 or Unity iPhone
#elif UNITY_IOS || UNITY_ANDROID || UNITY_WP8 || UNITY_IPHONE
//Check if Input has registered more than zero touches
if (Input.touchCount > 0)
{
//Store the first touch detected.
Touch myTouch = Input.touches[0];
//Check if the phase of that touch equals Began
if (myTouch.phase == TouchPhase.Began)
{
//If so, set touchOrigin to the position of that touch
touchOrigin = myTouch.position;
}
//If the touch phase is not Began, and instead is equal to Ended and the x of touchOrigin is greater or equal to zero:
else if (myTouch.phase == TouchPhase.Ended && touchOrigin.x >= 0)
{
//Set touchEnd to equal the position of this touch
Vector2 touchEnd = myTouch.position;
//Calculate the difference between the beginning and end of the touch on the x axis.
float x = touchEnd.x - touchOrigin.x;
//Calculate the difference between the beginning and end of the touch on the y axis.
float y = touchEnd.y - touchOrigin.y;
//Set touchOrigin.x to -1 so that our else if statement will evaluate false and not repeat immediately.
touchOrigin.x = -1;
//Check if the difference along the x axis is greater than the difference along the y axis.
if (Mathf.Abs(x) > Mathf.Abs(y))
//If x is greater than zero, set horizontal to 1, otherwise set it to -1
horizontal = x > 0 ? 1 : -1;
else
//If y is greater than zero, set horizontal to 1, otherwise set it to -1
vertical = y > 0 ? 1 : -1;
}
}
#endif
if (TCKInput.GetAction("jumpBtn", EActionEvent.Up))
{
animator.SetBool("jump", false);
}
if (TCKInput.GetAction("jumpBtn", EActionEvent.Down) && m_Grounded == true)
{
animator.SetBool("jump", true);
m_Grounded = false;
m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
}
if (TCKInput.GetAction("fireBtn", EActionEvent.Down))
{
animator.SetBool("swing", true);
m_Collider.enabled = !m_Collider.enabled;
}
if (TCKInput.GetAction("fireBtn", EActionEvent.Up))
{
animator.SetBool("swing", false);
m_Collider.enabled = !m_Collider.enabled;
}
if (TCKInput.GetAction("slideBtn", EActionEvent.Down))
{
if (timeStamp <= Time.time)
{
animator.SetBool("slide", true);
GameObject b = (GameObject)(Instantiate(bullet, transform.position + transform.right * 1.5f, Quaternion.identity));
b.GetComponent<Rigidbody2D>().AddForce(transform.right * 1000);
timeStamp = Time.time + 5;
}
}
if (TCKInput.GetAction("slideBtn", EActionEvent.Up))
{
animator.SetBool("slide", false);
}
if (TCKInput.GetAction("right", EActionEvent.Press))
{
move = -1;
}
if (TCKInput.GetAction("right", EActionEvent.Up))
{
animator.SetBool("run", false);
}
if (TCKInput.GetAction("left", EActionEvent.Press))
{
move = 1;
}
if (TCKInput.GetAction("left", EActionEvent.Up))
{
animator.SetBool("run", false);
}
if (objectCollider.IsTouching(anotherCollider))
{
canJump = true;
}
else
{
canJump = false;
}
}
private void Flip()
{
// Switch the way the player is labelled as facing.
m_FacingRight = !m_FacingRight;
// Multiply the player's x local scale by -1.
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
void Hurt()
{
move = 4;
SceneManager.LoadScene(0);
}
protected void OnCollisionEnter2D(Collision2D collision)
{
EnemyHealth3 enemy = collision.collider.GetComponent<EnemyHealth3>();
if (enemy != null)
{
move = 0;
animator.SetBool("death", true);
m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
StartCoroutine(ExecuteAfterTime(.1));
}
}
IEnumerator ExecuteAfterTime(double time)
{
yield return new WaitForSeconds((float)time);
Hurt();
}
}
#Code that is on the floor spawner script#
using UnityEngine;
using System.Collections;
public class Floor_Spawn_Script : MonoBehaviour
{
public GameObject[] obj;
private float oldPosition;
private float currentPosition;
private float ctr = 0;
private float inte = 10.19f;
// Use this for initialization
private void Start()
{
oldPosition = transform.position.x;
AddRoom(ctr * inte);
ctr += 1;
AddRoom(ctr * inte);
ctr += 1;
AddRoom(ctr * inte);
}
// Update is called once per frame
void Update()
{
currentPosition = transform.position.x;
if ((currentPosition - oldPosition) <= 9.595f)
{
AddRoom(ctr * inte);
oldPosition = transform.position.x;
ctr += 1;
}
}
void AddRoom(float roomCenter)
{
GameObject room = (GameObject)Instantiate(obj[Random.Range(0, obj.Length)]);
room.transform.position = new Vector3(roomCenter, 0f, 10f);
}
}```
I have created a script which was supposed to follow a specific path. Here is my script. But when I run my script nothing happens. Actually a capsule was supposed to move towards the child object of pathholder. I have tried using Vector3.movetoward but nothing happens
using System.Collections.Generic;
using UnityEngine;
public class Guard : MonoBehaviour {
public Transform pathholder;
private Vector3 startposition;
private Vector3 previousposition;
private float waittime=1.5f;
private Vector3[] waypoints;
Vector3 playerinitialposition;
void Start () {
Vector3[] waypoints = new Vector3[pathholder.childCount];
for (int x = 0; x < pathholder.childCount; x++) {
waypoints [x] = pathholder.GetChild (x).position;
}
startposition = pathholder.GetChild (0).position;
previousposition = startposition;
playerinitialposition = new Vector3 (startposition.x, this.transform.position.y, startposition.z);
this.transform.position = playerinitialposition;
StartCoroutine(Followpath(waypoints));
}
void Update () {
}
void OnDrawGizmos(){
foreach (Transform waypoint in pathholder) {
Gizmos.DrawSphere (waypoint.transform.position,1f);
Gizmos.DrawLine (previousposition, waypoint.position);
previousposition = waypoint.position;
}
}
IEnumerator Followpath ( Vector3[] waypoints){
int lengthinindex = 1;
Vector3 targetdestination = waypoints[lengthinindex];
while (true) {
Vector3.MoveTowards (this.transform.position, targetdestination, 5f * Time.deltaTime);
if (this.transform.position == targetdestination) {
lengthinindex = (lengthinindex + 1) % waypoints.Length;
targetdestination = waypoints [lengthinindex];
yield return new WaitForSeconds (waittime);
}
}
yield return null;
}
}
You need to assign the value returned by MoveTowards to your transform
transform.position = Vector3.MoveTowards (this.transform.position, targetdestination, 5f * Time.deltaTime);
You're calculating a new position with MoveTowards each time but never assigning it to the actual position of your object. MoveTowards Documentation.
I am using EnemyScript to move the enemy towards the player and killing the player, but I'm unable to spawn it randomly in code. I am currently spawning it directly through screen by placing the prefab on the scene.
Here is my EnemyScript
using UnityEngine;
using System.Collections;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
Destroy(shot.gameObject);
}
}
}
you could use something similar to this:
public GameObject myObj;
void Start ()
{
enemy = GameObject.Find("enemy");
InvokeRepeating("SpawnEnemy", 1.6F, 1F);
}
public void SpawnEnemy()
{
Vector3 position = new Vector3(Random.Range(35.0F, 40.0F), Random.Range(-4F, 2F), 0);
Instantiate(myObj, position, Quaternion.identity);
}
in the InvokeRepeating call you could possibly add the random range there also instead of a timed instantiate. This example is just a snippet of some prototype code i did a while ago, it may not suit your needs directly but hopefully will give you a general idea on what to do.
EDIT: to make sense, put this into a blank object somewhere in your scene, dont attach this to the actual enemy.
i programmed it like this.
public GameObject enemyPrefab;
public float numEnemies;
public float xMin = 20F;
public float xMax = 85F;
public float yMin = 3.5F;
public float yMax = -4.5F;
void Start () {
for (int i=0; i< numEnemies; i++) {
Vector3 newPos = new Vector3(Random.Range(xMin,xMax),Random.Range(yMin,yMax),0);
GameObject enemy = Instantiate(enemyPrefab,newPos,Quaternion.identity) as GameObject;
}
}
This will spawn a random number of enemies, at random locations, after random periods of time, all adjustable in the Inspector.
public float minTime = 1;
public float maxTime = 3;
public int minSpawn = 1;
public int maxSpawn = 4;
public Bounds spawnArea; //set these bounds in the inspector
public GameObject enemyPrefab;
private spawnTimer = 0;
Vector3 randomWithinBounds(Bounds r) {
return new Vector3(
Random.Range(r.min.x, r.max.x),
Random.Range(r.min.y, r.max.y),
Random.Range(r.min.z, r.max.z));
}
void Update() {
spawnTimer -= Time.deltaTime;
if(spawnTimer <= 0) {
spawnTimer += Random.Range(minTime, maxTime);
int randomSpawnCount = Random.Range(minSpawn, maxSpawn);
for(int i = 0; i < randomSpawnCount; i++) {
Instantiate(transform.transformPoint(enemyPrefab), randomWithinBounds(spawnArea), Quaternion.identity);
}
}
}
//bonus: this will show you the spawn area in the editor
void OnDrawGizmos() {
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = Color.yellow;
Gizmos.drawWireCube(spawnArea.center, spawnArea.size);
}
People on here seem to like using coroutines for time delays, but I personally prefer to track my own timers in Update() to maximize control and predictability.