I have a laser turret in Unity3D, which I'd like to turn towards the enemies. The turret consists of a "leg" and a "head" (selected on the picture 1). The head can pan and tilt around a spherical joint.
I do the following:
Vector3 targetDir = collision.gameObject.transform.position - turretHead.transform.position;
float step = turnSpeed * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(turretHead.transform.forward, targetDir, step, 0.0f);
turretHead.transform.rotation = Quaternion.LookRotation(newDir);
The problem is that since the pivot of the head is not aligned with the laser beam, the turret turns into the almost right direction, but it shoots above the target. (It would hit perfectly, if the laser would come out of the red axis of the pivot.)
Is there a builtin method or some trick to achieve the correct functionality other then doing the calculation myself?
Okay, here's the quick and easy way to do this. It's probably "better" to do it with proper trig, but this should give you the result you want pretty quick:
If you don't already have a transform aligned with the barrel, then create an empty GameObject and line it up (make sure it's a child of the turret so they move together). Add a reference to your script for it's transform.
Then, in your first line, calculate from the new Barrel transform instead of the turretHead transform. Leave everything else the same. This way it calculates from the turret barrel, but moves the turret head.
Now, this approach isn't perfect. If the pivot center is too offset from the barrel transform, then it would be less accurate over large moves, or when aiming at something close by, because the expected position when aiming would be different than the initial position due to the rotation pivot being elsewhere. But this can be solved with iteration, as the calculation would become more accurate the closer it is to it's desired goal.
Related
i am creating a 3d top down shooter with unity and wanted to create an aim assist for it since hitting enemies is difficult. my idea was to create a trigger that slows down the rotation of the joystick on the weapon when the player aims at an enemy. i implement my rotation as follows:
float eulerY = (Mathf.Atan2(_JoystickShoot.Direction.x, _JoystickShoot.Direction.y) * 180 / Mathf.PI);
However, I don't know how to implement the slowdown now. The player should rotate slower than the actual rotation of the joystick.
My approaches have not really been successful so far.
does anyone have any idea?
thanks :)
You can add a variable that scales the rotation. Set it to 1, except when you're pointing at an enemy, where you set it to a value between 0 and 1 that feels good for you.
float eulerY = rotationScaling * (Mathf.Atan2(_JoystickShoot.Direction.x, _JoystickShoot.Direction.y) * 180 / Mathf.PI);
If you want to draw the crosshair towards an enemy, you can calculate the angle (or distance) to the closest enemy. If it is below a certain threshold you can adjust your rotation function to favor rotations towards the enemy over rotations away from it. You can use something like this:
float eulerY = (Mathf.Atan2(_JoystickShoot.Direction.x, _JoystickShoot.Direction.y) * 180 / Mathf.PI);
if (closest enemy is within threshold range)
if (eulerY * (difference in Y angle to closest enemy) < 0) // if we rotate away from the enemy we scale the rotation down
eulerY *= 0.5;
else // if we rotate towards the enemy we scale it up
eulerY *= 1.5;
You can adjust this for multiple angles as well, but just doing the same calculation for X and Y direction might feel a bit awkward. The reason is that in that case you have to compare it against the "true" angle/distance, not against the X and Y angles/distances individually.
I think it is more useful to talk about 'direction' rather than 'rotation'.
Presumably your ship travels and/or shoots in the same direction as the joystick is pointing in. This is a natural and intuitive relationship. "slowing down rotation" will most likely break this, making your interface less intuitive to use. There are also usually ways to go directly from a direction-vector to a rotation, for example Quaternion.LookRotation, avoiding the need to deal angles directly.
You could instead check if there is any enemy within a small arc, select the enemy closest to the center of the arc, and use the direction towards that enemy to fire in. Note that this can fail if enemies are moving and shots are slow, since it can prevent 'leading' your shots.
Other alternatives could be to simply make your enemies easier to hit. Make the enemies or shots larger, or possibly, just make the hitboxes larger. Or increase fire-rate and/or dispersion.
The code that rotate the turret is kind simple:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotateturret : MonoBehaviour
{
public Transform target;
public Transform partToRotate;
public float turnSpeed = 10f;
private void Update()
{
transform.LookAt(target);
}
}
The target in this case is rotatingaround the turret in fixed speed but changing the height randomly.
If this script RotateTurret if I attach it to the Turret child part it will rotate and will look at the target. Like doing LookAt.
This is a screenshot of the Turret part:
And if I attach the script to the Pylon part it will rotate slowly and will always stay behind the target it will not rotate fast enough.
Screenshot of the Pylon part:
My problems are: What part of the turret should I rotate ? The logic say the Pylon I think.
Second is how should I calculate the speed the turret should rotate ? Does it always should facing perfect to the target ? Or sometimes if the target is moving in random speeds the turret will not facing it all the time 100% ? What is the logic in this case ? Just using LookAt is not enough. And I'm not using physics yet that's another problem I guess. If both turret and target will use physics but for now not sure how to do the turret logic (Or should I call it AI).
For testing I tried this script too:
public class Rotateturret : MonoBehaviour
{
public Transform target;
public Transform partToRotate;
public float turnSpeed = 10f;
private void Update()
{
Debug.DrawRay(transform.position, transform.forward, Color.green, 1000);
Vector3 dir = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles;
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
}
And again if I will rotate the Turret it will be more accurate but if the pylon it will be behind the target. And who said the speed of the rotating of the turret should be 10 and 20 or 50 or 1 ? There is no much logic in this script either I guess.
Another thing is that in the future I will want also to rotate the Gun to shoot bullets. So the rate fire and when to start firing that's another problem maybe that will be another question, But I guess the firing shooting bullets is also relevant to the turret rotating logic.
Firstly, note that from your screenshot, the Pylon gameobject's forward direction (blue arrow) is perpendicular to the turret barrel's direction. Thus, attaching your script on that gameobject will not produce your desired behaviour.
When it comes to the implementation of a turret's movement, it depends greatly on the context of your game.
For example, if the turret is to be used in a Tower Defence game where it should never miss, then typically the turret's rotation and shooting animation should just be an effect. Your actual damage dealing to the target will be done via script directly (e.g searching for viable target in range and sending an onShoot event). In this case, a transform.lookAt() is good enough, as it produces a reasonable visual effect.
However, if your turret is to be used in a First Player Shooter game, expect then that it's rotation will be controlled by the player (either via transform or rigidbody physics). Whether or not the turret should deal damage to the enemy depends on whether the projectile (simulated via physics with rigidbody and collider) hits the enemy or not. In this case, you have to limit your turret's maximum rotation speed. How much the threshold is depends on how realistic you want it to be.
Firstly, there is a LookRotation overload that takes the axis as a second parameter, so if you try
Quaternion lookRotation = Quaternion.LookRotation(dir,Vector3.up);
the rotation will always be around a vertical axis. This way you can split up motion into seperate axis as you would with a mechanical device (i.e. the pylon only rotates around a vertical axis)
As for the second part, i.e. how to make the turret hit the target, there's several approaches you can take:
You could, for example take the distance between the turret and the target, divide bullet velocity by target velocity, and take target.transform.forward multiplied by that ratio and the distance (this is from head, but you get the idea).
A bit more interesting alternative is to take the opportunity to learn about PID controllers, turrets are a fantastic example of how much can you do with a very simple controller.
PID stands for Proportional, Integral and Deriviative, and you compute your actuator output (i.e. force that drives your rotation) based on those three ways of looking at values.
In the most simplitied form it works like that: Proportional is your direct delta - how much of an angle away from your target angle you are (or from the target forward point). Than you average out that parameter (i.e. lerp the current value towards the target or using moving average) by a signifficant amount, this is your integral part - it will follow the target in a much slower way. Now if you subtract your averaged (integrated) value from your current (propoprtional) value, you get something that represents your change, this can be used as a deiviative component. Now this last part is what makes the turrent react to when the target is changing trajectory, you can make the output overshoot the change, or tune it in multiple other ways
I am trying to make a spin wheel that is divided into 6 sections. Each section is marked with a gameObject that is centered in that section. After the player spins the wheel, it will rotate until its starts stopping and then the wheel moves and stops in the center based on the section that was selected (Randomly). I used the following code to rotate the wheel towards the 0 on X axis. this code works fine and the wheel rotates fine, but only if the selected section was on the positive X axis.
float rotateFloat = (((randomReward + 1) * 60) - 30) - transform.rotation.z;
Quaternion targetRotation = Quaternion.Euler(new Vector3(0, 0, rotateFloat));
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, f_difX * Time.deltaTime);
I did some digging and found that Quaternion.RotateTowards()finds the closest way towards the target and rotates using that direction (This caused a problem with the direction of rotation).
Example (Follow image): The player swipes and randomReward (Number 5 on spin wheel) and the wheel starts rotating. When the wheel slows down, it starts moving towards the center. Then it will stop spinning along the direction of the swipe and will rotate towards the other side (Because the distance to the center is closer from the left side).
How can I set it to follow the same direction of the swipe regardless of which is closer to the center. And maybe if there is a better way to do this, please enlighten me. Thanks.
I think the easiest way to rotate a GameObject is by using:
float Speed = 1f;
void Update()
{
// Rotate the object around its local X axis at 1 degree per second
transform.Rotate(Vector3.right * Time.deltaTime * Speed);
}
You can read more about his here
It can happen sometimes the center of the GameObject it´s not placed in the center of the mesh or sprite. If you can´t modify this for any reason, what you can do is place this model/sprite as a child of an Empty GameObject and then attach the rotation script to the Empty GameObject.
While i understand that you don't want people to rotate the disk to the reward they want. Why do you use a random reward and go through the trouble of lining the rotation to the reward?
You should be able to say catch a 'swipe time', then do some math to it (say so it turns at least once if the screen touch time is very short) and then add a random range to it, that is within the circumference of the disk. Then when the disk is done spinning do a ray cast at the top location to determine the reward? (could also link the spinning time to the swipe-time, so that the reward is offered in somewhat the same time interval).
//this comment would have been made as a comment if i would have had the rights to do so, as i think this response may help the question asker, it is provided as an answer instead. i do hope this doesn't piss any one off to much :(
I have balls falling (gravity) in my game, and in its Update method I've also added a slight pull, as the wind was blowing:
transform.Translate(new Vector3(sidewaysDrag,0));
Now, I want to rotate my GameOBject around the Z axis as well (its a 2D game), ive tried:
transform.Rotate(Vector3.forward * Time.deltaTime * 200);
But I guess my anchor is wrong, because instead of rotating around itself the whole object is rotating in a very big curve. The rotation is correct if I comment out the translation...
Put your ball object under another empty object(parent).
And translate parent, rotate ball.
or translate ball's world position, rotate ball's localRotation
It's because you are using Vector3.forward, which actually points to Z-axis in world space, you need Z-axis in local space, you can get that by doing transform.TransformDirection(Vector3.forward);
Im making a 2D game where the player controls a tank.
I can make the tank, and all, but whats really messing with my mind is how to make it rotate accordingly.
I want it to behave just like the Wii game, Tanks.
Fixed directions, and with no real front and back on the tank.
Driving up, then left should make it rotate to the left.
Driving up, then down should not make it rotate, just drive the other direction.
I red a tutorial a while back about some way to do that by dividing the degrees into 2 180 degree parts. But i have simply not been able to find that damn site again.
I hope you guys are able to understand what im trying to say.
Thanks in advance :)
I assume you're drawing your tank as a sprite? In that case there's an overload of the SpriteBatch.Draw method that allow you to specify the rotation angle around the origin.
SpriteBatch.Draw overload
Here's an example on how to use it from MSDN
The example above will keep rotating your sprite, so you will need to add some custom logic so it will only rotate it according to keyboard input. Here's a simple example on how to check for keyboard input. So add logic that checks if the right or left button has been pressed, and update the rotation angle if they have. If it's the up or down button that has been pressed you simply modify the position of your sprite.
I hope it makes sense, otherwise just let me know.
I think what you're looking for is simply the best way to minimize the rotation of the tank, modulo 180 degrees.
I would use the angle between the desired movement direction and the tank's current direction to start. Make sure this is the minimum angle, then compare that with the angle between the tank's current direction + 180 degrees. Something like:
// smallest angle between the current direction and the desired direction
minAngle1 = Math.Abs(Math.Min(tankAngle - desiredAngle, desiredAngle - tankAngle));
// smallest angle between the opposite direction and the desired direction
oppositeAngle = (tankAngle + 180) % 360;
minAngle2 = Math.Abs(Math.Min(oppositeAngle - desiredAngle, desiredAngle - oppositeAngle));
// get the smaller of two to rotate to
if (minAngle1 < minAngle2) {
// we know that we should rotate the current direction to the desired direction
} else {
// rotate the opposing direction to the desired direction
}
Note you'll need to play with your rotation signs to ensure you're rotating the right way. Also, I've assumed you know your rotation angles, if you have vectors you can simplify this a little bit by using the dot product between the two vectors instead of the angle for comparisons.
Is your problem with the direction of movement based on the angle they have rotated?
Vector2 moveDir = new Vector2(Math.Cos(rotation), Math.Sin(rotation));
position += (moveDir * speed);
Speed here would be a number for how fast you want to move in that direction. position is another Vector2 for the position of the sprite. As Tchami says you can draw it with the rotation using the SpriteBatch.Draw overload. Rotation for the the Cos and Sin methods should be in radians but I think Draw should be in degrees if I remember correctly. MathHelper.ToRadians(degrees) and MathHelper.ToDegrees(radians) should solve that.
There is lots of XNA tutorials and examples on the site http://creators.xna.com/en-US/education/catalog/