I have a list of characters created in XNA C# as follows
for (int i = 0; i < GameConstants.NumDaleks; i++)
{
if (dalekList[i].isActive)
{
Vector3 line = dalekList[i].direction;
float rotationDal = (float)(Math.Atan2(dalekList[i].position.Y, dalekList[i].position.X) / (2 * Math.PI));
Matrix dalekTransform = Matrix.CreateScale(GameConstants.DalekScalar) * Matrix.CreateRotationY(rotationDal) * Matrix.CreateTranslation(dalekList[i].position);
DrawModel(mdlDalek, dalekTransform, mdDalekTransforms);
}
}
I'm trying to get characters to rotate to move in the direction they are facing. I believe this method is where I need to do but cannot figure out how to work out the angle require to go
Related
In my 2D game, the player Prefab has children which are weapons (like swords) so that when the player moves, the weapons translate in world space with his movements while maintaining a constant local position (until of course it is time to attack). The weapons automatically point towards nearby enemies, and swing when they get close enough.
I want the weapon to follow a swing arc by rotating around a pivot point defined at half of the weapon's range in the direction of the enemy. Once the weapon starts the swing, the arc's local position and rotation should remain unchanged and no longer care about the enemy position while the world arc will obviously translate with the player. The weapon should follow this arc purely relative to the player.
RotateAround seems to only work in world space, and thus causes strange problems when trying to rotate around an object in world space while the weapon's world position (as well as my desired pivot point's world position) would be translating with the player. Also, the point that I need it to rotate around needs to be relative to the local space, since when the player moves, the weapon needs to maintain its local arc while also translating with the player.
I also tried using Vector3.Slerp on the weapon's transform.localPosition, which seemed like it would be the perfect solution, but I can't seem to get the arc to match what I envision a good round swing would look like.
The attack consists of three parts: Backswing, Foreswing, and Recovery. The part that I care most about is the foreswing arc, as the others can be acheived easily with simply local rotations and lerping.
const int BACKSWING = 0;
const int FORESWING = 1;
const int RECOVER = 2;
float[] timeFrac = { .15f, .25f, .6f };
float[] rotations = { 120f, -240f, 120f };
float backSwingDistMultiplier = .5f;
//Swing Attack
public override IEnumerator Attack(float startAngle) {
var totalAttackTime = GetAttackTime();
var backSwingDist = backSwingDistMultiplier * Range;
var startPos = transform.localPosition;
var slerpCenterDiff = PerpDir(dir).normalized;
//Interpolation arrays
float[] swingTimes = { timeFrac[BACKSWING] * totalAttackTime,
timeFrac[FORESWING] * totalAttackTime,
timeFrac[RECOVER] * totalAttackTime };
float[] startAngles = { startAngle,
startAngle + rotations[BACKSWING],
startAngle + rotations[BACKSWING] + rotations[FORESWING] };
Vector3[] swingPositions = { startPos - (dir - slerpCenterDiff ) * backSwingDist,
startPos + dir * Range + slerpCenterDiff * backSwingDist };
Vector3[] slerpCenters = { (startPos + swingPositions[BACKSWING]) * .5f + slerpCenterDiff ,
((swingPositions[BACKSWING] + swingPositions[FORESWING]) * .5f) + slerpCenterDiff };
Vector3[] slerpStarts = { startPos - slerpCenters[BACKSWING],
swingPositions[BACKSWING] - slerpCenters[FORESWING]};
Vector3[] slerpEnds = { swingPositions[BACKSWING] - slerpCenters[BACKSWING],
swingPositions[FORESWING] - slerpCenters[FORESWING]};
timer = 0;
float percentDone;
//A swing attack has backswing, foreswing, and recovery
for (int swing = 0; swing <= 2; swing++) {
while (timer < swingTimes[swing]) {
percentDone = timer / swingTimes[swing];
//Backswing and Foreswing will slerp
if (swing < RECOVER) {
transform.localPosition = Vector3.Slerp(slerpStarts[swing], slerpEnds[swing], percentDone);
transform.localPosition += slerpCenters[swing];
} else { //Recover will lerp
transform.localPosition = Vector3.Lerp(swingPositions[FORESWING], startPos, percentDone);
}
transform.localRotation = Quaternion.Euler(0, 0,
startAngles[swing] + rotations[swing] * percentDone);
timer += Time.deltaTime;
yield return null;
}
transform.localRotation = Quaternion.Euler(0, 0, startAngles[swing] + rotations[swing]);
timer -= swingTimes[swing];
}
transform.localPosition = startPos;
}
I would just make an animation out of it, but I need the range to be dynamic, which is next to impossible to achieve with keyframes.
I was able to get my desired outcome by defining the arc centers in localPosition first:
var backswingArcCenter = (startPos + swingPositions[BACKSWING]) * 0.5f;
var foreswingArcCenter = (swingPositions[BACKSWING] + swingPositions[FORESWING]) * 0.5f;
and then calling RotateAround using that position added to the player's world space
if (swing == FORESWING) {
transform.RotateAround(player.transform.position + foreswingArcCenter,
Vector3.forward, Time.deltaTime / swingTimes[swing] * 270f);
} else if (swing == BACKSWING) {
transform.RotateAround(player.transform.position + backswingArcCenter,
Vector3.forward, Time.deltaTime / swingTimes[swing] * -180f);
}
Hello there everybody!
I'm trying to create a variable where you can put the number of points and the radius of a circle, and it will divide those points uniformly around the circle.
I'm trying to not use the Euler angles to set rotation or the Rotate or RotateAroundmethods.
But I am not having success...
These are how my code looks until the moment
public class PowerUps : MonoBehaviour
{
public GameObject PowerUpPrefab;
public GameObject Player;
public int PowerUpCount = 3;
public float PowerUpRadius = 1;
public Vector3 newPowerupSpace;
public GameObject[] SpawnPowerUps()
{
//get player positin
Vector3 anchorPoint = Player.transform.position;
GameObject[] SpawnPowerUps = new GameObject[PowerUpCount];
float angleStep = Mathf.HalfToFloat((ushort)(360.0 / PowerUpCount));
for (int i = 0; i < PowerUpCount; i++)
{
float theta = i * angleStep;
newPowerupSpace.x = anchorPoint.x + (PowerUpRadius * Mathf.Cos(theta));
newPowerupSpace.y = anchorPoint.y + (PowerUpRadius * Mathf.Sin(theta));
SpawnPowerUps[i] = (GameObject)Instantiate(PowerUpPrefab, newPowerupSpace, Quaternion.identity);
}
return null;
}
}
Any suggestions?
Update:
I changed the
float angleStep = Mathf.HalfToFloat((ushort)(360.0 / PowerUpCount));
to
float angleStep = ((ushort)((360.0 / PowerUpCount) * Mathf.Deg2Rad));
and now is working.
I feel kind of stupid....
Update 2:
After doing some tests, I notice that some numbers don't divide uniformly across the circle. That's because I was converting de circle degrees to radium in the wrong part.
Here's how the code looks like now:
public GameObject[] SpawnPowerUps()
{
Vector3 anchorPoint = Player.transform.position;
GameObject[] SpawnPowerUps = new GameObject[PowerUpCount];
float angleStep = ((ushort)(360.0 / PowerUpCount));
for (int i = 0; i < PowerUpCount; i++)
{
float theta = i * angleStep;
newPowerupSpace.x = anchorPoint.x + (PowerUpRadius * Mathf.Cos(theta * Mathf.Deg2Rad));
newPowerupSpace.y = anchorPoint.y + (PowerUpRadius * Mathf.Sin(theta * Mathf.Deg2Rad));
SpawnPowerUps[i] = (GameObject)Instantiate(PowerUpPrefab, newPowerupSpace, Quaternion.identity);
}
return null;
}
}
To describe a circle in 3D it should have three parameters, Position, Radius and Normal. The normal can be used to create a transform from the unit circle to the 3D space. This example uses instance methods instead of static methods for cross/dot/normalization, but it should be trivial to translate to the unity3D conventions.
First we create an arbitrary vector that is orthogonal to the normal:
public static Vector3 GetOrthogonal(this Vector3 self)
{
self = self.Normalized();
if (Math.Abs(self.Dot(Vector3.UnitX)) > 0.9)
{
return self.Cross(Vector3.UnitY).Normalized();
}
return self.Cross(Vector3.UnitX).Normalized();
}
We can then create two vectors that are orthogonal to the normal, and to each other:
var xAxis = normal.GetOrthogonal();
var yAxis = xAxis.Cross(normal).Normalized();
Getting the points is then simply multiplying each coordinate with the corresponding axis:
var x = xAxis * (PowerUpRadius * Mathf.Cos(theta * Mathf.Deg2Rad));
var y = yAxis * (PowerUpRadius * Mathf.Sin(theta * Mathf.Deg2Rad));
newPowerupSpace = x + y + anchorPoint;
You could do the same by producing a transform matrix and transforming the 2D points. But this is fairly simple to do.
the idea is how to align objects like in photo below, what formula need to use for this?
Now I can only do it this way, through sin or cos
void Start()
{
for (int i = 0; i < 50; i++)
characters.Add(Instantiate(characterPrefab, charactersParent));
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < characters.Count; i++)
{
float x = Mathf.Cos(i * verticalLines * Mathf.PI / characters.Count) * horizontalRadius;
characters[i].GetComponent<RectTransform>().anchoredPosition = new Vector3(x, -i * (1920f / characters.Count), 0);
}
}
In snake your rewards/bug eated are added on the opposite direction that the last body-part is facing.
The problem here is how you stablish your current snake movement, to get the opposite direction.
Let's guess that your movement is x += 1, then your opposite position will be your X position on your characters[characters.Count] and go on the opposite direction x -=1.
I am building a 3d game in Unity3D, where the use can draw on the ground when he is in top view.
The lines are drawn using the LineRenderer component.
I want to give the use the ability to select the lines he draw so he can delete them. But unfortunately I didn't find a good way to do it. If I am using 3D colliders, the only one that fits is a BoxCollider, but it's too big (I want the collider only on the line).
And I can't use the 2D colliders because they only work on XY plane.
I tried to convert the line into a mesh and just use MeshCollider but the line was too complex and the MeshCollider couldn't fit it self properly on the line.
Do you have any idea how can I do it¿
P.S.
I am selecting objects in the game with Ray casts.
if a MeshCollider is too complex, but a BoxCollider is not precise enough, try using multiple BoxColliders along your line segments.
An implementation can be found here:
https://answers.unity.com/questions/1546512/detect-mouse-click-on-line-renderer.html
If the link ever breaks, here is the code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(LineRenderer))]
public class LineCollider : MonoBehaviour
{
LineRenderer line;
public void Start() {
AddCollider(6); //Increase the count of parts(6) if you want to get more detailed collider
}
public void AddCollider(int part)
{
try
{
line = GetComponent<LineRenderer>();
var start = line.GetPosition(0);
var end = line.GetPosition(line.positionCount - 1);
var a = (line.positionCount - 1) / part;
for (int i = 1; i<=part; i++)
{
if (i == 1)
AddColliderToLine(start, line.GetPosition(Mathf.CeilToInt(a * 1)));
else if (i == part)
AddColliderToLine(line.GetPosition(Mathf.CeilToInt(a * (i - 1))), end);
else
AddColliderToLine(line.GetPosition(Mathf.CeilToInt(a * (i - 1))), line.GetPosition(Mathf.CeilToInt(a * i)));
}
}
catch
{
Destroy(gameObject);
}
}
private void AddColliderToLine(Vector3 start, Vector3 end)
{
var startPos = start;
var endPos = end;
BoxCollider col = new GameObject("Collider").AddComponent<BoxCollider>();
col.transform.parent = line.transform;
float lineLength = Vector3.Distance(startPos, endPos);
col.size = new Vector3(lineLength, 0.175f, 0.25f);
Vector3 midPoint = (startPos + endPos) / 2;
col.transform.position = midPoint;
float angle = (Mathf.Abs(startPos.y - endPos.y) / Mathf.Abs(startPos.x - endPos.x));
if ((startPos.y < endPos.y && startPos.x > endPos.x) || (endPos.y < startPos.y && endPos.x > startPos.x))
{
angle *= -1;
}
angle = Mathf.Rad2Deg * Mathf.Atan(angle);
col.transform.Rotate(0, 0, angle);
}
}
As an alternative, more performant but harder to implement: Write the lines into a texture and sample the pixels you clicked - each line should have a distinct color. This also assures you have some z-order when lines are drawn overlapping.
See my answer here to get an idea: How does Java / OS detect click co-ordinates on a viewport?
I have NPC's in my game that follow a script where they move randomly around the game. I would like them to face the direction they are moving in though.
for (int i = 0; i < GameConstants.NumDaleks; i++)
{
if (dalekList[i].isActive)
{
Vector3 line = dalekList[i].direction;
float rotationDal = (float)(Math.Atan2(dalekList[i].position.Y, dalekList[i].position.X) / (2 * Math.PI));
Matrix dalekTransform = Matrix.CreateScale(GameConstants.DalekScalar) * Matrix.CreateRotationY(rotationDal) * Matrix.CreateTranslation(dalekList[i].position);
DrawModel(mdlDalek, dalekTransform, mdDalekTransforms);
}
}
I'm sure it must be something to do with rotationDal, I have tried changing the calculation and the characters do seem to rotate differently, just not in their current direction
Xna has a built in function that you might find handy here.
Matrix.CreateWorld(positionVector, DirectionVector, UpVector);
here's the doc: http://msdn.microsoft.com/en-us/library/bb975261(v=xnagamestudio.40).aspx
In your case:
for (int i = 0; i < GameConstants.NumDaleks; i++)
{
if (dalekList[i].isActive)
{
Matrix dalekTransform = Matrix.CreateScale(GameConstants.DalekScalar) * Matrix.CreateWorld(dalekList[i].position, dalekList[i].direction, Vector3.Up);
DrawModel(mdlDalek, dalekTransform, mdDalekTransforms);
}
}