I am making 2D unity code where I put a random x value and get a gameobject to slowly move to that position. I cant use += or -= because the number can be larger or smaller than the vector. how do I fix this? here is the code.
public IEnumerator fistthrust()
{
float leftpos = Random.Range(-8, 8);
float rightpos = Random.Range(-8, 8);
while (fist.transform.position.x != leftpos && rightfist.transform.position.x != rightpos)
{
fist.transform.position = new Vector3(leftpos, 0, 0); -- fix these
rightfist.transform.position = new Vector3(rightpos, 0, 0); -- 2 lines
}
fistscr.down();
yield return new WaitForSeconds(0.5f);
rfistscr.down();
fistscr.up();
yield return new WaitForSeconds(0.5f);
rfistscr.up();
fistscr.rest();
yield return new WaitForSeconds(0.5f);
rfistscr.rest();
}
I am a beginner game designer and have no idea what to try or what to do. Please help!!
Looks like theres a MoveTowards function in the Vector3 Object https://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html That should do what you want.
So something like
Vector3 currentPosition = ...;
Vector3 targetPosition = ...;
// Use Vector3.MoveTowards to move towards the target
currentPosition = Vector3.MoveTowards(currentPosition, targetPosition, 3 * Time.deltaTime);
Would move the currentPosition 3 steps towards the target position.
Then you can simply add this in a while loop or something to move it smoothly over time
Related
I've got a setup right now where I have a 3D object with an empty object parented to it inside the object. When you press one button, an empty object that the camera is following rotates 90 degrees. If you press the other button, it rotates the other direction in 90 degrees. The result is that the camera can spin around the object 4 times before it makes a complete rotation.
Currently it works well, but I'm trying to figure out how I add some easing to the animation so it doesn't look so rough. I know a little about working with curves in animation but I'm not sure how I can apply that to code (or if it's even the best way to do things)
public InputMaster controls;
private bool isSpinning;
private float rotationSpeed = 0.3f;
IEnumerator RotateMe(Vector3 byangles, float intime)
{
var fromangle = transform.rotation;
var toangle = Quaternion.Euler(transform.eulerAngles + byangles);
for (var t = 0f; t < 1; t += Time.deltaTime / intime)
{
transform.rotation = Quaternion.Slerp(fromangle, toangle, t);
yield return null;
transform.rotation = toangle;
}
Debug.Log("finished rotation");
isSpinning = false;
Debug.Log("isSpinning now false");
}
code above is where I create a coroutine that says how it is going to transform. One thing that confuses me here a little is that if I don't have the line that says transform.rotation = toangle; , the rotation comes out at like 89.5 degrees or something, and if you do it multiple times it goes several degrees off. Not sure why that happens.
void Rotation(float amount)
{
Debug.Log("rotation number is " + amount);
float holdingRotate = controls.Player.Camera.ReadValue<float>();
if (holdingRotate == 1 && isSpinning == false)
{
isSpinning = true;
Debug.Log("isSpinning now true");
StartCoroutine(RotateMe(Vector3.up * 90, rotationSpeed));
}
else if (holdingRotate == -1 && isSpinning == false)
{
isSpinning = true;
Debug.Log("isSpinning now true");
StartCoroutine(RotateMe(Vector3.up * -90, rotationSpeed));
}
}
and this part is where the animation gets called up. Any help much appreciated.
Have a look at Animation Curves like #immersive said.
You can easily define your own curves in inspector
simply by adding this to your code:
public AnimationCurve curve;
You can then use AnimationCurve.Evaluate
to sample the curve.
The alternative is to use a premade library from the AssetStore/Package Manager like DOTween or LeanTween to name some.
Comments inline:
public class SmoothRotator : MonoBehaviour
{
// Animation curve holds the 'lerp factor' from starting angle to final angle over time
// (Y axis is blend factor, X axis is normalized 'time' factor)
public AnimationCurve Ease = AnimationCurve.EaseInOut(0, 0, 1, 1);
public float Duration = 1f;
private IEnumerator ActiveCoroutine = null;
public void RotateToward(Quaternion targetAngle) {
// If there's a Coroutine to stop, stop it.
if (ActiveCoroutine != null)
StopCoroutine(ActiveCoroutine);
// Start new Coroutine and cache the IEnumerator in case we need to interrupt it.
StartCoroutine(ActiveCoroutine = Rotator(targetAngle));
}
public IEnumerator Rotator(Quaternion targetAngle) {
// Store starting angle
var fromAngle = transform.rotation;
// Accumulator for Time.deltaTime
var age = 0f;
while (age < 1f) {
// normalize time (scale to "percentage complete") and clamp it
var normalisedTime = Mathf.Clamp01(age / Duration);
// Pull lerp factor from AnimationCurve
var lerpFactor = Ease.Evaluate(normalisedTime);
// Set rotation
transform.rotation = Quaternion.Slerp(fromAngle, targetAngle, lerpFactor);
// Wait for next frame
yield return null;
// Update age with new frame's deltaTime
age += Time.deltaTime;
}
// Animation complete, force true value
transform.rotation = targetAngle;
// Housekeeping, clear ActiveCoroutine
ActiveCoroutine = null;
}
}
for (var t = 0f; t < 1; t += Time.deltaTime / intime)
{
transform.rotation = Quaternion.Slerp(fromangle, toangle, t);
yield return null;
}
transform.rotation = toangle;
Because t won't equal to 1.
You just need to set the rotation to the target after the loop.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate : MonoBehaviour
{
public enum Axistorotate { Back, Down, Forward, Left, Right, Up, Zero };
public Vector3[] vectorAxises = new Vector3[7];
public Axistorotate[] myAxis;
public float angle;
public float speed;
private bool stopRotation = true;
// Start is called before the first frame update
void Start()
{
vectorAxises[0] = Vector3.back;
vectorAxises[1] = Vector3.down;
vectorAxises[2] = Vector3.forward;
vectorAxises[3] = Vector3.left;
vectorAxises[4] = Vector3.right;
vectorAxises[5] = Vector3.up;
vectorAxises[6] = Vector3.zero;
StartCoroutine(RotateObject());
}
public Vector3 GetAxis(Axistorotate axis)
{
return vectorAxises[(int)axis];
}
// Update is called once per frame
void Update()
{
if(Input.GetKeyDown(KeyCode.S))
{
stopRotation = false;
}
if (Input.GetKeyDown(KeyCode.C) && stopRotation == false)
{
stopRotation = true;
StartCoroutine(RotateObject());
}
}
IEnumerator RotateObject()
{
while (stopRotation == true)
{
for (int i = 0; i < myAxis.Length; i++)
{
transform.Rotate(GetAxis(myAxis[i]), angle);
}
yield return new WaitForSeconds(speed);
}
}
}
For some reason while the game is running and I'm changing one of the enums to forward or back or down each time it's rotating another direction. If I'm starting when both enums on back states and then changing one of them to down it looks like it's rotating to the left or right and then when changing back to back it's not rotating like it was when they were both set to back.
How can I update the vectorAxises array in real time while the game is running to show in the Inspector the current axis state for example : vectorAxises[0] -> 1, 0, -1 ..... vectorAxises[7] -> 0,-1, 0 I want that when I'm changing one of the enums that it will show it on the vectorAxises.
Maybe I need to create another vectorAxises array one for each enum ?
And maybe when doing two myAxis it's changing the same one the same angle so it's not realy two enums that change individual axis ?
transform.Rotate has an optional parameter
relativeTo
Determines whether to rotate the GameObject either locally to the GameObject or relative to the Scene in world space.
which by default is Space.Self.
So your
transform.Rotate(GetAxis(myAxis[i]), angle);
is always done in the local coordinate system of the GameObject. This local system is rotated along with the GameObject so the local transform.up, transform.forward etc axis change all the time.
Instead make it rotate around world axis
transform.Rotate(GetAxis(myAxis[i]), angle, Space.World);
How can I update the vectorAxises array in real time while the game is running to show in the Inspector the current axis state
This should already be the case. Or do you mean you want to see the currently "selected" value. You should use Debugging and Breakpoints for that. Since you do
for (int i = 0; i < myAxis.Length; i++)
{
transform.Rotate(GetAxis(myAxis[i]), angle);
}
without any further yield return the object will directly "jump" into the new rotation and in the inspector you would always only see the last GetAxis(myAxis[i]).
If you are looking for a smooth rotation then checkout Dest's answer slightly modified you could e.g. let the object rotate within 1 second
while (stopRotation == true)
{
// calculate the target rotation
Quaternion rotationQuaternion = Quaternion.identity;
for (int i = 0; i < myAxis.Length; i++)
{
rotationQuaternion *= Quaternion.AngleAxis(angle, GetAxis(myAxis[i]));
}
// before starting to rotate store initial and target rotation
var initialRotation = transform.rotation;
var targetRotation = initialRotation * rotationQuaternion;
// could also get this from the Inspector e.g.
var rotationDuration = 1;
// Do a smooth rotation from the initial to target rotation
// within the defined rotationDuration
var timePassed = 0f;
do
{
// additionally ease-in and -out the rotation
var lerpFactor = Mathf.SmoothStep(0, 1, timePassed / rotationDuration);
transform.rotation = Quaternion.Slerp(initialRotation, targetRotation, lerpFactor);
timePassed += Time.deltaTime;
// let this state be rendered
yield return null;
} while(timePassed < rotationDuration);
// if you still want the pause
yield return new WaitForSeconds(speed);
}
Just out of curiosity: Why even use an enum here? Couldn't you directly iterate through the vectorAxises index instead and only add those entries you will be using?
Use Quaternions for multiple rotations at the same time:
Quaternion rotationQuaternion = Quaternion.identity;
for (int i = 0; i < myAxis.Length; i++)
{
rotationQuaternion *= Quaternion.AngleAxis(angle, GetAxis(myAxis[i]));
}
transform.rotation *= rotationQuaternion;
It should fix your problem with wrong rotations
I'm trying to create MOB AI using rigidbody. I want to make the mob (GameObject) walk around the world using
mobrigid.AddForce((((goal - transform.position).normalized)*speed * Time.deltaTime));
(goal is a random place around mob).
here is where it gets complicated, some mobs fly up in the air at extreme speeds while others move normaly at slow speed. (And yes, I do make sure the goal.Y is a place on the ground and not air). I tried fixing it by changing the drag, but that leads to mobs walking on air and not falling with gravity.
I'm really lost, can't figure out how do I simply move a gameobject around with rigidbody without getting this odd behavior.
Logs of mobs (with Y of goal changed to 0):
Edit:
Image of the mob:
my movement logic:
case MOBTYPE.EARTHMOB:
switch (currentstatus)
{
case MOBSTATUS.STANDING:
if (mobrigid.IsSleeping())
{
mobrigid.WakeUp();
}
goal = this.transform.position;
goal.x = Random.Range(goal.x - 150, goal.x + 150);
goal.z = Random.Range(goal.z -150, goal.z + 150);
goal.y = 0;
currentstatus = MOBSTATUS.WALKING;
// Debug.Log("Transform:"+this.transform.position+ "Goal:" + goal+ "goal-transform:" + (goal - transform.position));
break;
case MOBSTATUS.WALKING:
if (Random.Range(1, 100) == 5)
{
currentstatus = MOBSTATUS.STANDING;
}
if (mobrigid.IsSleeping())
{
mobrigid.WakeUp();
}
mobrigid.AddForce((((goal - transform.position).normalized) * 10000 * Time.deltaTime));
// transform.LookAt(goal);
var distance = Vector3.Distance(goal, gameObject.transform.position);
if (distance <=5)
{
currentstatus = MOBSTATUS.STANDING;
}
break;
}
break;
Terrain Image:
The Rigidbody.AddForce function is used to add force to an Object along a direction. Since you have a position you need to move a Rigidbody to, you have to use the Rigidbody.MovePosition function. You may also want to mark the Rigidbody as kinematic.
A simple coroutine function to move a Rigidbody object to specific position:
IEnumerator MoveRigidbody(Rigidbody rb, Vector3 destination, float speed = 50f)
{
const float destThreshold = 0.4f;
while (true)
{
Vector3 direction = (destination - rb.position).normalized;
rb.MovePosition(rb.position + direction * speed * Time.deltaTime);
float dist = Vector3.Distance(rb.position, destination);
//Exit function if we are very close to the destination
if (dist <= destThreshold)
yield break;
yield return null;
//yield return new WaitForFixedUpdate();
}
}
It better to call this from another coorutine funtion so that you can yield or wait for it to finish then do other task like generating the random postion again and moving the Rigidbody there.
You want to generate new postion then move the Rigidbody there, this is an example of how to call the function above:
IEnumerator StartMoveMent()
{
Rigidbody targetRb = GetComponent<Rigidbody>();
while (true)
{
//Generate random position
Vector3 destination = new Vector3();
destination.y = 0;
destination.x = UnityEngine.Random.Range(0, 50);
destination.z = UnityEngine.Random.Range(0, 50);
//Move and wait until the movement is done
yield return StartCoroutine(MoveRigidbody(targetRb, destination, 30f));
}
}
And to start the StartMoveMent function:
void Start()
{
StartCoroutine(StartMoveMent());
}
While what I said above should work, I do recommend you use Unity's built in pathfinding system. Here is a tutorial for that. It simplifies finding paths to follow towards a destination with NavMesh which can also be baked during run-time.
I say that I am a beginner.
I have a question during the project.
I'm currently implementing a card-matching game.
When I start the game, The image is loaded from the external path (D: / ..).
My question is in the code below.
public void createCardList()
{
int count_x = 0;
int count_y = 0;
GameObject parent_area = GameObject.Find("GameMain");
List<int> num = createRandomIndex(createArrayIndex());
for (int i = 0; i < card_list.Count; ++i)
{
if ((i % CARD_ROWS) == 0 && i > 0)
{
count_y += 1;
count_x = 0;
}
GameObject card_prefab_load = Resources.Load("Prefabs/card") as GameObject;
GameObject card_prefab_instantiate = Instantiate(card_prefab_load, card_prefab_load.transform.position, Quaternion.identity);
float card_position_x = (CARD_WIDTH + CARD_SPACE) * (i % CARD_ROWS) - 290f;
//Debug.Log("card_position_x" + card_position_x);
float card_position_y = count_y * (CARD_HEIGHT + CARD_SPACE) - 280f;
//Debug.Log("card_position_y" + card_position_y);
card_prefab_instantiate.transform.SetParent(parent_area.transform);
card_prefab_instantiate.transform.name = "card_" + num[i];
card_prefab_instantiate.transform.localScale = new Vector3(1f, 1f, 1f);
card_prefab_instantiate.transform.localPosition = new Vector3(card_position_x, card_position_y, 1f);
StartCoroutine(firstRotateOriginalImage());
}
}
// Rotate image
private IEnumerator firstRotateOriginalImage()
{
yield return new WaitForSeconds(2f);
GameObject findCardList = GameObject.Find("GameMain");
for (int i = 0; i < findCardList.transform.childCount; ++i)
{
// I don't know what code to put in this part.
}
}
What I want to implement is below.
When the card rotation value reaches 90 degrees,How to change an externally imported image to a Sprite Image of a child GameObject?
How to rotate the child objects 360 degrees after the first task is completed?
For example, the picture below.
Arrows indicate the order in which cards are flipped.
also, Of the 12 GameObjects, Six GameObjects try to implement a common image.
I don't know much. I need your help.
There are many ways to do that...
transform.Rotate(...)
transform.RotateAround(...)
transform.localEulerAngles = transform.localEulerAngles.X|Y|Z +
amount
transform.localRotation = transform.localRotation*relativeRotation
/*= a Quaternion*/
Something else entirely...
I'm a doubtful regarding how well I understand your question...
But it seems like you want to simply flip the cards over don't you?
The approach I'd take is to have each card as the combination of the face (rotated 180 degrees in Y) and the back, both being children of an empty GameObject.
That way you could simply rotate the Card object using transform.Rotate(0, 180, 0)
To use it in a coroutine you could do
//Speed of animation (here 0.55 seconds)
float degreesPerSecond = 200f;
//The amount you want to rotate
float maxDegrees = 180f;
//Animate
for (float f = maxDegrees; f < 0;)
{
//How much to rotate
float rot = degreesPerSecond * Time.deltaTime;
//Rotate children
foreach(var child in transform)
child.Rotate(0, rot, 0);
//Record rotation
f -= rot;
//Wait next frame
yield return null;
}
//Round to desired rotation
foreach(var child in transform)
child.position = new Vector3(child.position.x, maxDegrees, child.position.z);
Been trying to code something for a while for a project im still working on for school, even though it's done.
I want to make a grenade that shoots out bullets on enemies colliding with it, but what I need help with atm is making 8 bullets shoots in all different directions at once.
here's the code my teacher gave me to do it(I asked him for help)
if (mouse.LeftButton == ButtonState.Pressed)
{
Texture2D pewTexture;
pewTexture = game.Content.Load<Texture2D>("pew");
game.firingSound.Play();
//Tweak pews to shoot at mouse, rather than just going straight on the y axis.
pew = new CPew(game, pewTexture);
pew.velocity = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * 10f + spriteVelocity;
pew.position = position + pew.velocity * 5;
//easy cheesy way:
//pew.position = position;
//pew.position.X += 15f;
//pew.position.Y -= 20f;
//super awesome cool way for cool people and celebrities
pew.position = position +
new Vector2(
texture.Width * .2f - pew.texture.Width * .2f,
-pew.texture.Height);
game.pews.Add(pew);
myFiringDelay = 10;
}
else
{
if (mouse.RightButton == ButtonState.Pressed)
{
float pi2 = (float)Math.PI * 2.0f;
int numShots = 10;
float pi2overnum = pi2 / numShots;
for (int i = 0; i < numShots; i++)
{
Vector2 direction = PiToVec2(pi2overnum * i);
//particles[i].reset(position,direction, vector2.zero, 6);
game.pews[i].Reset(pew.position, direction, Vector2.Zero);
}
Vector2 PiToVec2(float piT)
{
return new Vector2((float)Math.Sin(piT), (float)Math.Cos(piT));
}
apparently this will make it shoot in every direction on mouse right click but every time I try it and my game crashes straight up
Then we have a pew class which is bullets and thats what I want to be shot in those directions at the same time
you guys may not be able to help with the code i've shown you, i've spent a while looking for a way to do this but i can't seem to find anything
A former example and or source code would be really helpful, just at least another way to look at this thanks.
Showingme when it crashed, it tells me index goes out of range or is negative, if you guys could just show me base code for multidirectional bullets id be happy
The problem your having is that when you try and loop through your game.pews collection you are trying to call Reset() on the first 10 items in the collection. However there appear to not be 10 pews in there. So when you get to the end and try to access the next one, that's where you get the "Index out of range" error.
For your 8 bullets from a grenade part of the question, I think you want to do something like this.
//Once a grenade has collided with an enemy
float pi2 = (float)Math.PI * 2.0f;
int numShots = 8;
float pi2overnum = pi2 / numShots;
for (int i = 0; i < numShots; i++)
{
Vector2 direction = PiToVec2(pi2overnum * i);
pew = new CPew(game, pewTexture);
pew.velocity = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * 10f + spriteVelocity;
//Set the position based off of the grenades last position.
//Set the direction.
//Set any other attributes needed.
game.pews.Add(pew);
}
Now you have eight bullets moving in different directions from where the grenade exploded.
To update them and draw them I would recommend using a foreach loop so you don't have to worry about "Index out of range" errors.
foreach CPew pew in game.pews
{
pew.Update();
}