I'm going to make track follower.
and here's my track in unity
Track image
I want to operate codes 1 ~ 10 at step by step in unity C# script method void Update{},
but I can't find how to make it.
I make rotation code using with trigonometric systems, Sin and Cos.
Rigidbody rigidbody;
public GameObject ball;
Vector3 P1 = new Vector3(-5, 1.5f, 25);
public float theta;
// Start is called before the first frame update
void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
ball.transform.position = Vector3.MoveTowards(transform.position, P1, 0.05f); // 1st move
if(theta <210) // 2nd rotation with trigonometic equations
{
theta += 1;
ball.transform.position = Rot(theta);
}
}
Vector3 Rot(float theta) // rotation by trigonometric equations
{
Vector3 P2;
P2.x = (3 * Mathf.Cos(theta * Mathf.PI / 180) - 3 * Mathf.Sin(theta * Mathf.PI / 180)) -8;
P2.y = 1.5f;
P2.z = (3 * Mathf.Sin(theta * Mathf.PI / 180) + 3 * Mathf.Cos(theta * Mathf.PI / 180)) + 22 ;
return P2;
}
if I play that code, the ball just warp to position P1 and immediately start rotation
how can i use that?
and can't use keyboard or something else input handler, only use by codes
To debug in unity you will need to use visual studio debug tool. Click on the play button "Connect with Unity" and play on the unity side. You will of course need to put a debug point where you need it.
Note: Your game will be stuck during the Update debug, if you can and you need to see update in the game view just execute the code that you need to debug in a coroutine.
If you just need to know the value of a variable you should be able to debug it without any problem even in the update.
Related
I'm new to Unity and am working on a 2D top down shooter. I have written a script for the enemy to rotate to the player when they have a line of sight. What I am trying to do now is make it so that when the player enters the enemies line of sight, the enemy doesn't instantly snap facing the player and instead rotates quickly to face them. My current code to do this is in the while loop of my script and causes Unity to crash when the player enters line of sight of the enemy, any idea what I'm doing wrong? My script works fine without the while loop and its contents BTW.
'''c#
private void FixedUpdate()
{
Vector2 playerDir = playerLocation.position - this.transform.position;
float angle = (Mathf.Atan2(playerDir.y, playerDir.x) * Mathf.Rad2Deg);
if (canSeePlayer())
{
while (angle - rb.rotation < 10f || angle-rb.rotation > 10f)
{
rb.MoveRotation(rb.rotation + 1);
}
rb.MoveRotation(angle - 90);
}
}
private bool canSeePlayer()
{
RaycastHit2D hit = Physics2D.Raycast(this.transform.position, playerLocation.position - this.transform.position);
Debug.DrawRay(this.transform.position, playerLocation.position - this.transform.position);
Debug.Log(hit.collider.gameObject);
if (hit.collider.gameObject.CompareTag("Player"))
{
return true;
}
else
{
return false;
}
'''
many physics component it work on fixed update
and many method NOT make change immediately
for example :
you can test by using this code
var rotation = Rigidbody.rotation; // before
Rigidbody.MoveRotation(5);
var newRotation = Rigidbody.rotation; // after
after you call rigibody.moveRotation (on line 2) the rotation is still equal newRotation and you will see update on the next fixed update
that why Unity crash (becuase infinite while loop, you rotation not update)
For more information :
https://docs.unity3d.com/Manual/ExecutionOrder.html
I believe Mathf.Lerp is what you're looking for, it allows to smooth out changes in values without overshooting them. In your FixedUpdate try this:
Vector2 playerDir = playerLocation.position - this.transform.position;
float angle = (Mathf.Atan2(playerDir.y, playerDir.x) * Mathf.Rad2Deg);
float rotationSpeed = 1f;
rb.rotation = Mathf.Lerp(rb.rotation, angle, rotationSpeed);
Of course you are welcome to adjust rotationSpeed to your liking, as well as extract it to a class variable editable within the editor.
Regarding your game freezing, please note that your while exit condition is not so well defined:
while (angle - rb.rotation < 10f || angle-rb.rotation > 10f)
Note that if you'll start with an angle less by 10 than rb.rotation you end up increasing rb.rotation furthermore in the loop thus creating an endless loop with no exit condition.
Example:
angle = 0 and rb.rotation = 15f
0. angle - rb.rotation = -15 (less than 10f) => execute loop => rb.rotation++
1. angle - rb.rotation = -16 (less than 10f) => execute loop => rb.rotation++
2. angle - rb.rotation = -17 (less than 10f) => execute loop => rb.rotation++
3. ...
Once I saw a Google Play game where there was such
good physics. I'm new in Rigidbody2D move and physics. How can I do
the same physics? Now my bullet just explodes and don't boost tank. When I added Rigidbody2D, bullet pushed my tank
on a little bit, but that's all, my tank stops.
I have only default Rigidbody2D move script.
void FixedUpdate()
{
if (moveup1) // bool button for touch controls
{
direction = Mathf.Sign(Vector2.Dot(rb.velocity, rb.GetRelativeVector(Vector2.right)));
rb.AddRelativeForce(-Vector2.left * upspeed * 300); // these vectors looks weird but all work perfectly
}
if (movedown1) // bool button
{
direction = Mathf.Sign(Vector2.Dot(rb.velocity, rb.GetRelativeVector(Vector2.right)));
rb.AddRelativeForce(Vector2.left * downspeed * 300);
}
if (rotateleft1) // bool button
{
steeringAmount = -1;
rb.rotation += steeringAmount * steeringPower;
rb.AddRelativeForce(-Vector2.right * rb.velocity.magnitude * steeringAmount / 2);
}
if (rotateright1) // bool button
{
steeringAmount = 1;
rb.rotation += steeringAmount * steeringPower;
rb.AddRelativeForce(-Vector2.right * rb.velocity.magnitude * steeringAmount / 2);
}
}
Also
numbers should I put in the Rigidbody2D parameters? Now I'm using these params:
I hope for your understanding and patience. Thanks!
Lower Mass and Linear Drag. Also add Force when you instantiate the bullet. Force direction should be negative shooting direction.
If you want to push the tank on impact, the direction would be Vector3 dir = tank.position - impact.position. However the vector is longer on bigger distances, but we want the opposite (the close the explosion, the more force). So we can use float force = 1f / dir.magnitude and then apply it like this
rb.addForce(dir.normalized * force);
In general there is Rigidbody.AddExplosionForce but it's not available for 2D.
right now I am trying to make rts camera zooming with panning when close to the ground. The problem I have now is that I use mouse scroll wheel for zooming and it makes the zooming feel like it's laggy. It looks like it jumps some Y value and teleports rather than smoothly move to the desired position. Also, I would like to know how to make the camera stop at the minimum Y value because what is happening now is that it stops at around 22 rather than 20 which is my minimum Y value for the camera movement.
I tried zooming with + and - on my numpad and it worked how I wanted (smoothly zooming in and out without skipping) but not all players have numpad and I feel it is more suitable to zoom with mouse wheel.
{
private const int levelArea = 100;
private const int scrollArea = 25;
private const int scrollSpeed = 25;
private const int dragSpeed = 70;
private const int zoomSpeed = 50;
// Maximum/minimum zoom distance from the ground
public int zoomMin = 20;
public int zoomMax = 120;
private const int panSpeed = 40;
// Minimal/maximal angles for camera
private const int panAngleMin = 30;
private const int panAngleMax = 90;
void Update()
{
// Init camera translation for this frame.
var translation = Vector3.zero;
// Zoom in or out
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
if (zoomDelta != 0)
{
translation -= Vector3.up * zoomSpeed * zoomDelta;
}
// Start panning camera if zooming in close to the ground or if just zooming out.
var pan = transform.eulerAngles.x - zoomDelta * panSpeed;
pan = Mathf.Clamp(pan, panAngleMin, panAngleMax);
// When to start panning up the camera
if (zoomDelta < 0 || transform.position.y < (zoomMax -20))
{
transform.eulerAngles = new Vector3(pan, 0, 0);
}
// Move camera with arrow keys
translation += new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Move camera with mouse
if (Input.GetMouseButton(2)) // MMB
{
// Hold button and drag camera around
translation -= new Vector3(Input.GetAxis("Mouse X") * dragSpeed * Time.deltaTime, 0,
Input.GetAxis("Mouse Y") * dragSpeed * Time.deltaTime);
}
else
{
// Move camera if mouse pointer reaches screen borders
if (Input.mousePosition.x < scrollArea)
{
translation += Vector3.right * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.x >= Screen.width - scrollArea)
{
translation += Vector3.right * scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y < scrollArea)
{
translation += Vector3.forward * -scrollSpeed * Time.deltaTime;
}
if (Input.mousePosition.y > Screen.height - scrollArea)
{
translation += Vector3.forward * scrollSpeed * Time.deltaTime;
}
}
// Keep camera within level and zoom area
var desiredPosition = transform.position + translation;
if (desiredPosition.x < -levelArea || levelArea < desiredPosition.x)
{
translation.x = 0;
}
if (desiredPosition.y < zoomMin || zoomMax < desiredPosition.y)
{
translation.y = 0;
}
if (desiredPosition.z < -levelArea || levelArea < desiredPosition.z)
{
translation.z = 0;
}
// Move camera parallel to world axis
transform.position += translation;
}
}
I would like to have a smooth transition from the position where the camera is now and the desired position after scrolling in/out. And also I would like to know how to make the camera to stop at the minimum/maximum zoom distance rather than to stop close to it. Thank you for helping. Video how the camera movement looks like: https://youtu.be/Lt3atJEaOjA
Okay, so I'm going to do three things here. First and foremost, I'm going to recommend that if you're working with advanced camera behavior, you probably want to at least consider using Cinemachine. I'd walk you through it myself, but given my lack of personal experience with it, I'd probably be doing you a disservice by even trying. There are many good tutorials out there. Youtube and Google should provide.
The second thing I'll do is solve your problem in the most direct way I can manage, and after that, we'll see if we can't come up with a better method for solving your problem.
So, the key here is that Unity's scrollwheel input is pretty binary. When you check a scrollwheel axis, the result is directly based on how many "clicks" your wheel has gone through since the last frame update, but what you really want is something with a bit of give. By default, Unity can actually do this with most of its axis inputs: You might notice that if you use WASD in a default Unity project, there's a sort of "lag" to it, where you'll take your fingers off the keys but you'll still keep receiving positive values from Input.GetAxis() for a few frames. This is tied to the Gravity value in your input settings, and Input.GetAxisRaw() is actually used to circumvent this entirely. For whatever reason, scrollwheel axes don't seem to be affected by axis gravity, so we essentially have to implement something similar ourselves.
// Add this property to your class definition (so it persists between updates):
private float wheelAxis = 0;
// Delete this line:
var zoomDelta = Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * Time.deltaTime;
// And put these three new lines in its place:
wheelAxis += Input.GetAxis("Mouse ScrollWheel");
wheelAxis = Mathf.MoveTowards(wheelTotal, 0f, Time.deltaTime);
var zoomDelta = Mathf.Clamp(wheelAxis, -0.05f, 0.05f) * zoomSpeed * Time.deltaTime;
Right, so we do a few things here. Every update, we add the current scrollwheel values to our wheelAxis. Next, we apply the current Time.deltatime as "gravity" via the Mathf.MoveTowards() function. Finally, we call what's mostly just your old zoomDelta code with a simple modification: We constrain the wheelAxis with Mathf.Clamp to try and regulate how fast the zooming can happen.
You can modify this code in a couple of ways. If you multiply the Time.deltaTime parameter you can affect how long your input will "persist" for. If you mess with the Mathf.Clamp() values, you'll get faster or slower zooming. All in all though, if you just want a smooth zoom with minimal changes to your code, that's probably your best bet.
So!
Now that we've done that, let's talk about your code, and how you're approaching the problem, and see if we can't find a somewhat cleaner solution.
Getting a good camera working is surprisingly non-trivial. Your code looks like a lot of code I see that tries to solve a complex problem: It looks like you added some feature, and then tested it, and found some edge cases where it fell apart, and then patched those cases, and then tried to implement a new feature on top of the old code, but it kinda broke in various other ways, etc etc, and what we've got at this point is a bit messy.
The biggest issue with your code is that the camera's position and the camera's rotation are closely tied together. When working with characters, this is usually fine, but when working with a camera, you want to break this up. Think about where the camera is and what the camera is looking at as very separate things to keep track of.
So here's a working camera script that you should be able to plug in and just run with:
using UnityEngine;
public class RTSCamera : MonoBehaviour
{
public float zoomSpeed = 100f;
public float zoomTime = 0.1f;
public float maxHeight = 100f;
public float minHeight = 20f;
public float focusHeight = 10f;
public float focusDistance = 20f;
public int panBorder = 25;
public float dragPanSpeed = 25f;
public float edgePanSpeed = 25f;
public float keyPanSpeed = 25f;
private float zoomVelocity = 0f;
private float targetHeight;
void Start()
{
// Start zoomed out
targetHeight = maxHeight;
}
void Update()
{
var newPosition = transform.position;
// First, calculate the height we want the camera to be at
targetHeight += Input.GetAxis("Mouse ScrollWheel") * zoomSpeed * -1f;
targetHeight = Mathf.Clamp(targetHeight, minHeight, maxHeight);
// Then, interpolate smoothly towards that height
newPosition.y = Mathf.SmoothDamp(transform.position.y, targetHeight, ref zoomVelocity, zoomTime);
// Always pan the camera using the keys
var pan = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * keyPanSpeed * Time.deltaTime;
// Optionally pan the camera by either dragging with middle mouse or when the cursor touches the screen border
if (Input.GetMouseButton(2)) {
pan -= new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y")) * dragPanSpeed * Time.deltaTime;
} else {
var border = Vector2.zero;
if (Input.mousePosition.x < panBorder) border.x -= 1f;
if (Input.mousePosition.x >= Screen.width - panBorder) border.x += 1f;
if (Input.mousePosition.y < panBorder) border.y -= 1f;
if (Input.mousePosition.y > Screen.height - panBorder) border.y += 1f;
pan += border * edgePanSpeed * Time.deltaTime;
}
newPosition.x += pan.x;
newPosition.z += pan.y;
var focusPosition = new Vector3(newPosition.x, focusHeight, newPosition.z + focusDistance);
transform.position = newPosition;
transform.LookAt(focusPosition);
}
}
While I encourage you to go through it in your own time, I'm not going to drag you through every inch of it. Instead, I'll just go over the main crux.
The key idea here is that rather than controlling the camera's height and orientation directly, we just let the scrollwheel dictate where want the camera's height to be, and then we use Mathf.SmoothDamp() to move the camera smoothly into that position over several frames. (Unity has many useful functions like this. Consider Mathf.MoveTowards() for an alternative interpolation method.) At the very end, rather than trying to fiddle with the camera's rotation values directly, we just pick a point in front of us near the ground and point the camera at that spot directly.
By keeping the camera's position and orientation completely independent of each other, as well as separating out the "animation" of the camera's height, we avoid a lot of headaches and eliminate a lot of potential for messy interwoven bugs.
I hope that helps.
Cinemachine is pretty good, but I suggest people learn on your own quite a bit before using Cinemachine so that you better understand what's going on in the background.
while (transform.position != new Vector3(desX, desY))
{
// 2 - Movement
Vector3 movement = new Vector3(
0.1f * desX,
0.1f * desY,
0);
//movement *= Time.deltaTime;
transform.Translate(movement);
}
This part of my program crashes the Unity engine and I'm pretty sure it's an infinite loop but I can't figure out why or how to fix it.
It's freezing your application because you're are not giving other scripts chance to run when the condition in the while loop is not met.
To fix that put that code in a coroutine function then add yield return null; to the while loop. This makes Unity to wait for a frame after each loop therefore given other scripts the opportunity to run every frame. This should fix the freeze issue whether the while loop exits or not. I would also suggest you use Vector3.Distance to determine when you are close to the destination.
public float reachThreshold = 0.2f;
void Start()
{
StartCoroutine(MoveBject());
}
IEnumerator MoveBject()
{
float distance = Vector3.Distance(transform.position, new Vector3(desX, desY));
while (distance > reachThreshold)
{
// 2 - Movement
Vector3 movement = new Vector3(
0.1f * desX,
0.1f * desY,
0);
//movement *= Time.deltaTime;
transform.Translate(movement);
//Wait a frame
yield return null;
}
}
If you really want to move GameObject to another position over time, see this post.
In general don't do things in while that are meant to happen in a per frame base! (Thanks Ron)
The result of while in the best case would be that your App stucks until eventually the vectors match, making the object "jump" in position. In the worst case they never match and your App freezes forever.
Instead you should use the Update() method, which is called each frame, and just move the object one step per frame.
To compare the Vectors you should better use Vector3.Distance. Using the == operator for Vector3 is basically ok but internally it does something equal to
Vector3.Distance(vectorA, vectorB) <= 0.00001f
which uses a very small thershold that might not match exactly. So Using Vector3.Distance you can set your own threshold e.g. to 0.1 to make it "easier" to match.
bool match = Vector3.Distance(transform.position, new Vector3(desX, desY) < 0.1f
To move an object towards another Unity actually already has a built-in method Vector3.MoveTowards e.g.
transform.position = Vector3.MoveTowards(transform.position, new Vector3 (desX, desY), 0.1f);
This takes care of Vectors3 comparing so you don't even need it anymore.
To make it smooth and framarate-save you were right already to use Time.deltaTime. Without this it would move 0.1 meters / frame but your framerate might not be stabil. Using Time.deltaTime makes it 0.1 meters / second which in almost all cases is what you actually want to achieve.
So to put it together your code should be something like
float desX;
float desY;
float moveSpeed = 0.1f;
void Update()
{
var targetPosition = new Vector3 (desX, desY);
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
}
Note that in some cases MoveTowards still is not relayable enough e.g. if you want to track collisions or something. In this case refer here
for the game I 'm creating I was trying to create a mega start that take you 750 meters ahead, but when trying to do this with vector3.lerp this is done instantly , and after trying many things he could not get it to work.
Here is the script that i wrote
if (in750Run)
{
PlayerManager.Instanse.gameObject.transform.position = Vector3.Lerp(PlayerManager.Instanse.gameObject.transform.position,PowerUpFinalePlayer.position, Time.deltaTime * 5);
Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, PowerUpFinaleCamera.position, Time.deltaTime * 5);
if (Vector3.Distance(PlayerManager.Instanse.gameObject.transform.position, PowerUpFinalePlayer.position) > 2)
{
in750Run = false;
}
}
If you have a better way of doing this, please replay to this post :)
1) If you need smooth rotate to the target use:
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
2) To set a speed:
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;