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);
}
Related
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 ;)
So I have this piece of code that generates a 7x50 grid that falls down.
public class FieldController : MonoBehaviour
{
[SerializeField] private GameObject gridPrefab;
[SerializeField] private int rows = 50;
[SerializeField] private int columns = 7;
[SerializeField] private float tileSize = 1;
private GameObject tile;
private GameObject parentObject;
private float gridW;
private float gridH;
private float posX;
private float posY;
private void Start()
{
parentObject = new GameObject("Parent");
GenerateGrid();
}
private void Update()
{
MoveGridDown();
}
void GenerateGrid()
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
tile = Instantiate(gridPrefab, parentObject.transform);
posX = j * tileSize;
posY = i * tileSize;
tile.transform.position = new Vector2(posX, posY);
}
}
SetParentToStart(parentObject);
}
void SetParentToStart(GameObject parent)
{
parent.transform.position = new Vector3(-columns / 2 + tileSize, rows / 10, 0);
}
void MoveGridDown()
{
parentObject.transform.position -= new Vector3(0, FallingObjectBehaviour.FallingSpeed * Time.deltaTime);
}
}
This is how it looks:
https://imgur.com/a/nKtOuyV
What I want to do is to be able to generate the new grid every time the previous one reaches the end so it would look like the grid is infinite.
I've tried to use extra GameObject for a parent and switch between them but the results were buggy.
Is there any way to make it possible?
i believe you want to generate the next grid before it reaches the end, and i suggest a system where you have
GameObject gridPrefab;
GameObject grid;
Queue<GameObject> grids;
Transform camera;
Vector3 newPos = Vector3.zero;
Vector3 offset = new Vector3(0,0,50);
Quaternion forward = Quaternion.Euler(Vector3.forward);
int i = 0;
then you instantiate the new grid once camera is beyond "newPos", add it to the queue and destroy the last one.
if(newPos.z > camera.z)
{
newPos += offset;
grid = Instantiate(gridPrefab,newPos,forward);
grid.name = $"grid{++i}";
grids.Enqueue(grid);
Destroy(grids.Dequeue());
}
this is done without testing, let me know if makes sense to you, i can test it later if makes sense but is not working.
I have a script that spawns my ground prefabs infinitely. The only problem is that occasionally there is a "bump" for the player to hit at the boundary of a new ground prefab.
It seems like one prefab is slightly higher than the previous one and when the player hits that small bump they go flying. How do I fix this?
I might just make the player a game object instead of a rigidbody and animate it instead of using actual physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundManager : MonoBehaviour
{
//oH is object height
public GameObject[] ground;
public GameObject[] obstacles;
private Transform playerTransform;
private float spawnZ = 0.0f;
private float spawnO = 0.0f;
public float oH;
private float tileLength = 40.0f;
private float oLength = 36.0f;
private int tilesOnScreen = 8;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < tilesOnScreen; i++)
{
SpawnTile();
SpawnObstacle();
}
}
// Player must be tagged "Player"
void Update()
{
if (playerTransform.position.z > (spawnZ - tilesOnScreen * tileLength))
{
SpawnTile();
SpawnObstacle();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
go = Instantiate(ground[0]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private void SpawnObstacle()
{
GameObject go;
go = Instantiate(obstacles[Random.Range(0, obstacles.Length)]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = new Vector3(0, oH, 1 * spawnO);
spawnO += oLength;
}
}
This code works to infinitely spawn the ground objects but has the bumps that I described. Just one bump is enough to screw up the whole game.
I am making a Game in unity3d. I am using a mini-map where I want to change the size of my minimap camera with the orthographic feature. if the distance between player and enemy is greater than some value then the size of minimap camera will be greater and if the distance is less than something then the size will be lesser how can I do that in with using mathf.clamp function if we limit camera size to some maximum value?
This video is addressing same problem as you want. Here is the link this will help you.
https://www.youtube.com/watch?v=llEJtLuQyPM&index=3&list=PLclF8feY8EKLw5Un6Z2Syt2_35qsdpnHt
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public float m_DampTime = 0.2f;
public float m_ScreenEdgeBuffer = 4f;
public float m_MinSize = 6.5f;
[HideInInspector] public Transform[] m_Targets;
private Camera m_Camera;
private float m_ZoomSpeed;
private Vector3 m_MoveVelocity;
private Vector3 m_DesiredPosition;
private void Awake()
{
m_Camera = GetComponentInChildren<Camera>();
}
private void FixedUpdate()
{
Move();
Zoom();
}
private void Move()
{
FindAveragePosition();
transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
}
private void FindAveragePosition()
{
Vector3 averagePos = new Vector3();
int numTargets = 0;
for (int i = 0; i < m_Targets.Length; i++)
{
if (!m_Targets[i].gameObject.activeSelf)
continue;
averagePos += m_Targets[i].position;
numTargets++;
}
if (numTargets > 0)
averagePos /= numTargets;
averagePos.y = transform.position.y;
m_DesiredPosition = averagePos;
}
private void Zoom()
{
float requiredSize = FindRequiredSize();
m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
}
private float FindRequiredSize()
{
Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);
float size = 0f;
for (int i = 0; i < m_Targets.Length; i++)
{
if (!m_Targets[i].gameObject.activeSelf)
continue;
Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);
Vector3 desiredPosToTarget = targetLocalPos - desiredLocalPos;
size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.y));
size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.x) / m_Camera.aspect);
}
size += m_ScreenEdgeBuffer;
size = Mathf.Max(size, m_MinSize);
return size;
}
public void SetStartPositionAndSize()
{
FindAveragePosition();
transform.position = m_DesiredPosition;
m_Camera.orthographicSize = FindRequiredSize();
}
}
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.