This question already has an answer here:
Move GameObject back and forth
(1 answer)
Closed 5 years ago.
I'm trying to move multiple objects, simultaneously from Point A to Point B and back again, looped to serve as obstacles in blocking the player.
I've tried
StartCoroutine(Oscillate(OscillationFunction.Sine, 1f));
public IEnumerator Oscillate(OscillationFunction method, float scalar)
right = GameObject.FindGameObjectsWithTag("MovingObs2");
foreach (GameObject r in right)
{
startPos = r.transform.position;
v = startPos;
v.x = (r.transform.position.x + (Mathf.Cos(Time.time) * scalar));
r.transform.position = v;
yield return new WaitForSeconds(2);
r.transform.position = startPos;
}
and others, but they're all difficult to contain within a desirable distance and speed from the starting position. It's too fast, and too far.
and I tried a seemingly simpler line, which is easier to understand, for me.
v.x = l.transform.position.x + speed * Time.deltaTime;
l.transform.position = v;
but since i'm using an array in a foreach loop, i don't know how to preserve each GameObjects' transform.position, so that it can be used as a condition to reverse the direction of the objects' movement every time it reaches either Point A, or point B.
if (l.transform.position.x <= startPos.x || l.transform.position.x >= startPos.x + endPos.x)
{
speed *= -1;
}
edit: I apologize if I asked a duplicate question, I thought it was different due to the number of objects involved in an array.
I tried to assume to resolve all your question, please see the code below.
Time.deltaTime is not needed, since your code (and my) using the Time.time to calculate each time the position of the object.
I also would recommend (if possible) to not put all in on script, but give each object a swing script and also try to not use the CoRoutine. But to answer your question I show you how to do it.
I have created an internal class, to store in the "Init" the targeted gameobjects and their startPosition in a list. Later you can simply loop throu this list and always have the start position.
You need an endless loop in the "Osciallate" routine and need to wait each turn. In your example you put the Wait on the wrong place, so it waits after moving each object and stops after the first run throu all of this objects.
Here the code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OscilateObs : MonoBehaviour {
// internal class where all gameobjects and their startposition will be saved
internal class OscGameObjects
{
public GameObject gameObject;
public Vector3 startPosition;
}
// Here the information of all gameObjects stored in a list
private List<OscGameObjects> objectList;
public float updateSpeed = 0.05f;
public float oscScalar = 2f;
public enum OscillationFunction {
Sine = 1
}
void Start () {
// First, the gameobjects have to saved to our internal List
InitializeOscGameObjects();
// Start the Corotine
StartCoroutine(Oscillate(OscillationFunction.Sine, oscScalar));
}
private void InitializeOscGameObjects()
{
var objects = GameObject.FindGameObjectsWithTag("MovingObs2");
objectList = new List<OscGameObjects>();
foreach (var o in objects)
{
var oscObject = new OscGameObjects();
oscObject.gameObject = o;
oscObject.startPosition = o.transform.position;
objectList.Add(oscObject);
}
}
public IEnumerator Oscillate(OscillationFunction method, float scalar)
{
// Loop forever
while(true)
{
foreach (var element in objectList)
{
var currentPosition = element.gameObject.transform.position;
currentPosition.x = element.startPosition.x + Mathf.Cos(Time.time) * scalar;
element.gameObject.transform.position = currentPosition;
}
yield return new WaitForSeconds(updateSpeed);
}
}
}
EDIT:
I forgot to meantion:
1) I would recommend to use "Animation" Component for the movement and not using C# at all, so you can change the behaviour if needed and you are more flexible.
2) I would also recommend if possible to make a parent "GameObject" and moving only this and put the "MovingOb2" simply as child objects.
EDIT2:
Adding a delay increment for each object, so they not running syncron:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OscilateObs : MonoBehaviour {
// internal class where all gameobjects and their startposition will be saved
internal class OscGameObjects
{
public GameObject gameObject;
public Vector3 startPosition;
public float waitCount;
}
// Here the information of all gameObjects stored in a list
private List<OscGameObjects> objectList;
public float updateSpeed = 0.05f;
public float oscScalar = 2f;
public float waitIncrementTime = 0.01f;
public enum OscillationFunction {
Sine = 1
}
void Start () {
// First, the gameobjects have to saved to our internal List
InitializeOscGameObjects();
// Start the Corotine
StartCoroutine(Oscillate(OscillationFunction.Sine, oscScalar));
}
private void InitializeOscGameObjects()
{
var objects = GameObject.FindGameObjectsWithTag("MovingObs2");
objectList = new List<OscGameObjects>();
float i = 0;
foreach (var o in objects)
{
i += waitIncrementTime;
var oscObject = new OscGameObjects();
oscObject.gameObject = o;
oscObject.startPosition = o.transform.position;
oscObject.waitCount = i;
objectList.Add(oscObject);
}
}
public IEnumerator Oscillate(OscillationFunction method, float scalar)
{
// Loop forever
while(true)
{
foreach (var element in objectList)
{
var currentPosition = element.gameObject.transform.position;
currentPosition.x = element.startPosition.x + Mathf.Cos(Time.time + element.waitCount) * scalar;
element.gameObject.transform.position = currentPosition;
}
yield return new WaitForSeconds(updateSpeed);
}
}
}
they're all difficult to contain within a desirable distance and speed from the starting position. It's too fast, and too far
Before you start monkeying with the structure of your code, I suggest you stick with the cosine function but educate yourself on the (very simple) manner in which you can manipulate output frequency and amplitude. This knowledge will serve you well (if this question is typical of your average project) and will solve the problem trivially.
If it's too fast, you can reduce the frequency (or increase the wavelength/period) by multiplying the input (which is always absolute time) by a constant coefficient between 0.0 and +1.0 (not inclusive).
If it's too far, you can reduce the amplitude by multiplying the output by a constant coefficient between 0.0 and +1.0 (not inclusive).
You just need to pick two constants and adjust to taste by running the program and seeing how you like it.
Related
I'm making a game in which I want to player collect coins using a magnet
here is the coins spawning code
// Spawn Coins In Air
void SpawnCoinsInAir ()
{
float yPos = transform.position.y + 1f;
float zPos = transform.position.z + 4f;
for (int i = 0 ; i < 10 ; i += 1)
{
int line = i == 0 ?
RandomsPlayerController.Instance.CurrentLineIdx
:
Random.Range(0, RandomsPlayerController.Instance.Lines.Count);
for (int j = 0 ; j < Random.Range(3,6) ; j += 1)
{
zPos += j + 10f;
var it = Instantiate(
coinTransform,
new Vector3(RandomsPlayerController.Instance.Lines [line].x,yPos , zPos),
Quaternion.identity
);
// do random rotattion in y
it.transform.DORotate (
Utilities.VectorY (Vector3.zero, Random.Range(-360,360)),
1f,
RotateMode.FastBeyond360
)
.SetEase (Ease.InOutSine);
Destroy (it, actionDuration + 1f);
}
}
}
Now I got all my coins in my game and I have to find each coin by its tag and collect them in the shortest distance.
I'm wondering how much cost it takes to find all coins in the Update function OR is there any way to do the same thing by keeping performance in mind?
Here is the code
private void UseMagnet ()
{
// collect coins
foreach (var coin in GameObject.FindGameObjectsWithTag ("coin")) continue;
}
private void Update () => UseMagnet();
Thanks in Advance
For smaller games, the cost is insignificant to the player. But it tends to get exponential the larger the search-space it.
How much cost GameObject.FindGameObjects
This is a case-by-case answer, use the profiler to see what is causing the most lag in your game.
Though unity documentation did state For performance reasons, it is recommended to not use this function every frame.
OR is there any way to do the same thing by keeping performance in mind?
Yes, what you are looking for is called caching.Create a static list and store all the coins there, like so:
// NOTE: Alternatively, you can turn this into a singleton
public static class GlobalCache {
// Transform or Coin Object.
private static HashSet<Transform> coinCache = new HashSet<Transform>();
public static Transform FetchAnyCoin() {
if (coinCache.Count <= 0) {
// Create a new coin, return it;
// NOTE: Ideally, creation of coins into this cache should be done else-where.
// The 'cache' should only handle storing and get/set requests.
return CreateNewCoin();
}
var result = coinCache.First();
// You can remove the fetched coin from the cache if you like.
coinCache.Remove(result);
return result;
}
}
In contrary to the generic Find, which you should never use if there is any other option, the FindGameObjectsWithTag as the name says uses a hashed (pre-indexed) tag which is quite optimized and not too expensive.
Of course there is still other ways to go which are even faster.
I would use a collection so the type itself can keep track of its own instances:
public class Coin : MonoBehaviour
{
private static readonly HashSet<Coin> instances = new ();
public static IEnumerable<Coin> Instances => instances;
private void Awake()
{
// will automatically register itself when coming to live
instances.Add(this);
}
private void OnDestroy()
{
// will automatically unregister itself when destroyed
instances.Remove(this);
}
}
Now you can simply iterate all coins via e.g.
foreach(var coin in Coin.Instances)
{
// check if close enough for your magnet e.g.
if(Vector3.Distance(coin.transform.position, player.transform.position) <= magnetRange)
{
//TODO: e.g. add points ?
Destroy(coin.gameObject);
}
}
It is considered fairly costly and is advised against using it in the Update function.
Essentially what you wish to do is to have a list over all spawned coins, and you control the spawning. This means you could easily add the spawned object to the list when you spawn it, and - if needed - remove them when you destroy them (if you dont remove them, you need to check for null in the list).
Not perfect, but depending on what you need it might work for you (not perfect because you're looping through a list asynchronously as you're adding/removing things from it)
For simplicity's sake, let's add a static (accessible anywhere) list somewhere
public static List<GameObject> SpawnedCoins = new List<GameObject>();
var it = Instantiate<GameObject>(coinTransform, ...);
SpawnedCoins.Add(it);
StartCoroutine(RemoveCoin(it))
IEnumerator RemoveCoin(GameObject coin, float time) {
yield return new WaitForSeconds(time);
SpawnedCoins.Remove(coin);
Destroy(coin);
}
Another class
foreach (var coin in SpawnedCoins) {
// Check for null first, if you Destroy them they will be null in the list
}
Alternatively, Destroy and check for null and then every few seconds clear the list of nulls before running the loop.
I have a spawner object. Every time a gameobject is spawned, I want that object to move randomly (wandering). The problem in my script is that the gameobject movement is very random (jittery). How can I solve this?
void Start ()
{
InvokeRepeating("SpawnNPC", Spawntime, Spawntime);
}
// Update is called once per frame
void Update () {
population = GameObject.FindGameObjectsWithTag("NPCobject");
for (int i = 0; i < population.Length; i++)
{
getNewPosition();
if (population[i].transform.position != pos)
{
population[i].transform.position = Vector3.MoveTowards(population[i].transform.position, pos, .1f);
}
}
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
I made the New randomize vector in different method, because I plan to change it with pathfinder function and make it in different thread/task.
You are choosing a new direction every single frame. That will always be very jittery. You wan't to only change direction after, at least, a small interval. Here is a link to one way to do that from Unity's website. https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html
What about using Navigation? As you said wandering, I thought it would give you a nice result and also make your code simple.
The following screenshot is a sample with Navigation. The moving game objects are also changing their direction nicely, although it cannot be seen in the sample because the game object is capsule...
Ground game object in the sample program has NavMesh. See here to build NavMesh.
Agent game object has NavMeshAgent Component. See here to set it up.
Th Behaviour class below is for Agent game object.
using UnityEngine;
using UnityEngine.AI;
public class NavAgentBehaviour : MonoBehaviour {
public Transform[] Destinations { get; set; }
// Use this for initialization
void Start ()
{
InvokeRepeating("changeDestination", 0f, 3f);
}
void changeDestination()
{
var agent = GetComponent<NavMeshAgent>();
agent.destination = Destinations[Random.Range(0, Destinations.Length)].position;
}
}
The next Behaviour class is just for spawning the Agent and setting up the destinations. On Unity, set it to whatever game object in the scene, and allocate game objects to the fields.
using UnityEngine;
public class GameBehaviour : MonoBehaviour {
public GameObject Agent;
public Transform SpawnPoint;
public Transform Destination1;
public Transform Destination2;
public Transform Destination3;
// Use this for initialization
void Start()
{
Agent.SetActive(false);
InvokeRepeating("Spawn", 0f, 2f);
}
void Spawn() {
var newAgent = Instantiate(Agent);
newAgent.GetComponent<NavAgentBehaviour>().Destinations = new[] { Destination1, Destination2, Destination3 };
newAgent.transform.position = SpawnPoint.position;
newAgent.SetActive(true);
}
}
Increase the number of destination, to make the moves look more random. By the way, the destinations do not need to be specified by game objects, which is only for making it easy to see the sample program's behaviour.
The source of the jitteriness comes from the fact that you are updating the position to move every frame so your objects never have a consistent location to move to. I would instead suggest attaching a new script to each of your objects that individually handles their movement. In that script you could do something like the following, which has a delay to keep the target position for more than 1 frame.
float delaytimer;
Vector3 pos;
void Start () {
getNewPosition(); // get initial targetpos
}
void Update () {
delaytimer += Time.deltaTime;
if (delaytimer > 1) // time to wait
{
getNewPosition(); //get new position every 1 second
delaytimer = 0f; // reset timer
}
transform.position = Vector3.MoveTowards(transform.position, pos, .1f);
}
void getNewPosition()
{
float x = Random.Range(-22, 22);
float z= Random.Range(-22, 22);
pos = new Vector3(x, 0, z);
}
You are changing the direction they are moving in every frame, thats what is causing the jitter.
You could wait a few moments before you change the direction, perhaps something like this.
// Outside update
float betweenChanges = 2;
float lastChange = 0;
// Inside update
if(Time.realtimeSinceStartup > lastChange)
{
// Change directions
// ...
lastChange = Time.realTimeSinceStart + betweenChanges;
}
You could also solve this by using InvokeRepeating or a Coroutine.
If you dont want all the NPC's to change direction at the same time and still plan on controlling every NPC from the same class like in your example, you should perhaps add a timer for each NPC instance instead and use that to decide when to change its direction.
A better idea would be to let each NPC have its own Movement-script.
I am creating a zoo simulation game in Unity 5.4.0f3 using C#. I am trying to spawn a bear prefab clone, do some math, wait for a period of time, do some more math, then destroy the clone object all in one function. Here is what I have right now. The math that I am trying to do in WaitForBearLife() does not happen. Thanks in advance!
using UnityEngine;
using System.Collections;
public class BuyBearButton : MonoBehaviour
{
[SerializeField] GameManager gameManager;
[SerializeField] GameObject bearPrefab;
[SerializeField] Visitor visitor;
GameObject bearClone;
float xMin = -15;
float xMax = 15;
float yMin = 5;
float yMax = 6;
public void BuyBear()
{
Vector2 pos = new Vector2 (Random.Range (xMin, xMax), Random.Range (yMin, yMax));
if (gameManager.myMoney >= gameManager.bearCost)
{
gameManager.numberOfBears++;
gameManager.myMoney = gameManager.myMoney - gameManager.bearCost;
visitor.spawnTime = visitor.spawnTime / visitor.bearAttraction;
bearClone = (GameObject) Instantiate (bearPrefab, pos, transform.rotation);
StartCoroutine (WaitForBearLife ());
Destroy (bearClone, gameManager.bearLife);
}
}
IEnumerator WaitForBearLife()
{
yield return new WaitForSeconds (gameManager.bearLife);
visitor.spawnTime = visitor.spawnTime * visitor.bearAttraction;
}
}
you need to move Destroy function from BuyBear to WaitForBearLife
The problem is that now StartCouroutine call immediately returns and Destroy() gets called right away
I've just copied your code in an empty project, and made placeholder classes for GameManager and Visitor, just with random values in the variables we need in this script. It works just fine... really.So, I'll make you some questions (as I don't have the reputation yet to comment in your post...)
First, there's a script in the bear prefab of any sorts?
This is because it maybe have something that could interrupt the coroutine. It seems unlikely, but you can't know for sure until you try.
Next, how is the declaration of Visitor and GameManager?
The very beginning, when you define the class name and such. In fact, I would like to know more about the variables you used, like gameManager.bearLife and such.
Now, let's see the values.
I've tried with:
public int myMoney = 50000;
public int bearCost = 50;
public int numberOfBears = 0;
public float bearLife = 2.0f;
In GameManager. And:
public float spawnTime = 5;
public float bearAttraction = 2;
In Visitor. It does some weird math, and the logic of this script itself is clearly in development right now, I suppose. But the point is, it works, and with this:
IEnumerator WaitForBearLife()
{
yield return new WaitForSeconds (gameManager.bearLife);
print (visitor.spawnTime);
visitor.spawnTime = visitor.spawnTime * visitor.bearAttraction;
print (visitor.spawnTime);
}
I get 2.5 and 5 in the correspondant prints if I only click once. Some prints (As #Benjamin-lecomte stated) could help you to know if it's even executing that part.
For now, I can't help you without knowing much more. So I'll wait for your answer, then.
I'm creating a game in unity 2D and in this game a GameObject called Moving_Truck is required to smoothly move into the scene from that left side. as this will be required later, I tried to make the method run from an other code on another object, the object is called scene control and the script is called opening scene.
the problem is when I push the space button the Moving_Truck game object does not move. I am fairly new to C# and have tried a few solutions such as Vector2.MoveTowards and Vector2.Lerp. I have also modified my code multiple times trying to get this to work. here is the most recent version of the codes:
CharacterBase
using UnityEngine;
using System.Collections;
public class CharacterBase : MonoBehaviour {
private float SprSize, HalfSprSize, Distance;
public int run = 1;
public void CharMove(int Dir, string Char, float speed, string BackgroundName)
{
var CharOBJ = GameObject.Find(Char);
var BGOBJ = GameObject.Find(BackgroundName);
SprSize = CharOBJ.GetComponent<Renderer>().bounds.size.x;
HalfSprSize = SprSize / 2;
Vector2 EndPos = new Vector2(BGOBJ.transform.position.x, CharOBJ.transform.position.y);
Debug.Log(EndPos);
CharOBJ.transform.position = Vector2.MoveTowards(CharOBJ.transform.position, EndPos, speed * Time.deltaTime);
}
}
OpeningScene
using UnityEngine;
using System.Collections;
public class OpeningScene : CharacterBase {
int Advance = 0, Run = 0;
void Start ()
{
}
void FixedUpdate()
{
if (Input.GetKeyUp("space"))
{
Run = 1;
Debug.Log("Space Pressed");
}
if (Run == 1)
{
Run = 0;
Advance += 1;
switch (Advance)
{
case 1:
CharMove(-1, "Moving_Truck", 0.05f, "House_Front");
break;
case 2:
CharMove(1, "Moving_Truck", 0.05f, "House_Front");
break;
}
}
}
}
This is driving me nuts, I've been trying to fix it for about an hour or Two now, can someone please help, also sorry for the long question, just comment if you need more info. also please ignore the Dir Argument for now.
Thanks.
Unity's Input.GetKeyUp only returns true on the frame when you release the spacebar. Because of this, CharMove will only be called that one frame you press the spacebar, and then only move 0.05f * timeDelta, which is probably going to be less than a pixel.
Also, this is unrelated, but you don't want to call GameObject.Find(string) every time you move the character. Instead, call it once in the Start() method and then store the result to a field.
Have you tried
GetComponent<Rigidbody2D> ().velocity = new Vector2 (moveSpeed, GetComponent<Rigidbody2D> ().velocity.y);
I'm working on Unity 5, and I need to create a list of transforms in order to modify them in the inspector (position and rotation) to emulate a different camera. This emulation is for a demo, like a camera moving on its own during the demo.
I currently have a list of transforms, but I don't know how to make them modifiable in the inspector and change their position at runtime?
Edit: 18/11/15
Here is the solution that if find with a help of a friend of mine more aware of what really does unity, hope it will help you and thanks again for all your reply it helped me a lot :D :
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MoveCamera : MonoBehaviourSingleton<MoveCamera>
{
public List<Transform> cameraPositions = new List<Transform>();
private Transform m_Target;
private float m_Speed;
private bool m_Translate;
private bool m_Rotate;
public void SwitchToNext(int index, float speed)
{
m_Target = cameraPositions[index];
m_Translate = true;
m_Rotate = true;
}
public void Update()
{
if (m_Target != null && (m_Translate || m_Rotate))
{
float ratio = Time.deltaTime * m_Speed;
transform.position = Vector3.Lerp(transform.position, m_Target.position, ratio);
transform.rotation = Quaternion.Slerp(transform.rotation, m_Target.rotation, ratio);
if (Vector3.Distance(transform.position, m_Target.position) < 0.001f)
{
transform.position = m_Target.position;
m_Translate = false;
}
if (Quaternion.Angle(transform.rotation, m_Target.rotation) < 0.001f)
{
transform.rotation = m_Target.rotation;
m_Rotate = false;
}
}
}
}
Ok lets see if this helps you.(i will write my code assuming you want this)
You have x gameObjects in your scene.
You will need a GameObject List. Lets say you get them by doing
public List<GameObject> myObjectList = new List<GameObject>();
public List<Transform> myTransformList = new List<Transform>();
myObjectList = GameObject.FindGameObjectsWithTag("YourCustomTagHere");
foreach(Gameobject g in myObjectList)
{
myTransformList.Add(g.transform);
}
I dont know if this code works, i cant test it right now, but i think the logic is there. Go ahead and try a few things, give some feedback whether you made it or not.
I would suggest just a simple public list in your MoveCamera script and then dragging the targets into that list. Doing it this way you have full control over the order of the objects and thus you can do animated transitions from camera position n to n + 1.
public List<Transform> cameraPositions = new List<Transform> ();
Maybe a simple sequence does not fit your requirements and you need a more sophisticated way to decide which position is allowed to switch to which other position. In this case I'd suggest a helper script e.g. CameraTransition.cs. Here you can place your check logic and definition parameters.
Attach this to every allowed target position object and replace the list in MoveCamera by List<CameraTransition>.