I'm provide LineRenderer to draw the shape of Room in 2D and store the LineRenderer position values in DrawLine.storeLineRenderers list.(List2>)
My plan to create 3D room using 2D Shape , i.e LineRenderer's.
Following code gives convert to 3d, but not accurate
for(int i=1;i<=noOfLines;i++)
{
LineRenderer line =GameObject.Find("line" + i).GetComponent<LineRenderer>();
if(line!=null)
{
Vector3[] ss= DrawLine.storeLineRenderers[i - 1];
print(ss[0]); //Position Element 0 value (Vector3)
print(ss[1]); //Position Element 1 value (Vector3)
//generate 3d box (wall) here
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "wall" + i;
cube.transform.localPosition =new Vector3(ss[0].x,ss[0].y,ss[0].z);
cube.transform.localScale = new Vector3(ss[1].x - ss[0].x, ss[1].y - ss[0].y,ss[1].z - ss[0].z);
}
}
Output
How to solve this?? Is there any formula to create properly??
thanks in advance...
try this out.
What you have to be aware of is that the cube's pivot is in its center, meaning you have to add half its size to its position to make it "start" at ss[0] and span all the way to ss[1].
Also, the wall need to be rotated to look at ss[1].
const float thickness = .1f; // how thick will the wall be
const float height = 1f; // how high will the wall be
Vector3 dist = ss[1] - ss[0];
float length = dist.magnitude; // make wall long enough to span ss[0] to ss[1]
Vector3 cubeSize = new Vector3(thickness, height, length);
Vector3 cubePos = ss[0] + (cubeSize * 0.5f); // cube pivot is in its center
Quaternion cubeOrientation = Quaternion.LookRotation(dist, Vector3.up); // rotate to look at ss[1]
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localPosition = cubePos;
cube.transform.localScale = cubeSize;
cube.transform.localRotation = cubeOrientation;
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);
}
First, i will explain myself...
I was trying to make an script for the camera to follow the player and keep itself inside the Map.
After a litle research i found out that you can use a property of tileMap called tileMap.localBounds.min to find the bottomLeft corner and tileMap.localBounds.max to find the topRight corner of a tilemap.
The problem with that is that my map is not a tileMap, it`s a sprite.
So i try the following...
public Transform target;
public GameObject theMap;
private Vector3 bottomLeftLimit;
private Vector3 topRightLimit;
// These values are for the camera
private float halfHeight;
private float halfWidth;
void start()
{
target = PlayerController.instance.transform;
halfHeight = Camera.main.orthographicSize;
halfWidth = halfHeight * Camera.main.aspect;
// We assign the corners of our map
// Maybe we`ll have to change the image for a tileMap to use: tileMap.localBounds
bottomLeftLimit = theMap.GetComponent<SpriteRenderer>().sprite.bounds.min + new Vector3(halfWidth, halfHeight, 0f);
topRightLimit = theMap.GetComponent<SpriteRenderer>().sprite.bounds.max + new Vector3(-halfWidth, -halfHeight, 0f);
// We send the bound limits to the PlayerController script to keep the player inside the map
PlayerController.instance.SetBounds(theMap.GetComponent<SpriteRenderer>().sprite.bounds.min, theMap.GetComponent<SpriteRenderer>().sprite.bounds.max);
}
void LateUpdate()
{
transform.position = new Vector3(target.position.x, target.position.y, transform.position.z);
// Keep the camera inside the bounds
transform.position = new Vector3(Mathf.Clamp(transform.position.x, bottomLeftLimit.x, topRightLimit.x), Mathf.Clamp(transform.position.y, bottomLeftLimit.y, topRightLimit.y), transform.position.z);
}
But it seems that the value of sprite.bounds.max and sprite.bounds.min are always 0 (the center), so it doesn`t work to set the bounds for the camera.
Anyone can help? I would aprecciate it a lot...
SpriteBoundsFinder.cs
using UnityEngine;
using UnityEngine.UI;
public class SpriteBoundsFinder : MonoBehaviour
{
public Image image;
private void Update()
{
var rectTransform = image.GetComponent<RectTransform>();
var sizeDelta = rectTransform.sizeDelta;
var position = image.transform.position;
var bottomLeft = position - new Vector3(sizeDelta.x / 2, sizeDelta.y / 2);
var topRight = position + new Vector3(sizeDelta.x / 2, sizeDelta.y / 2);
Debug.Log($"bottomLeft: {bottomLeft}, topRight: {topRight}");
}
}
You can use this to find the bottom left and top right corner of a sprite in world space. Assuming you're using an overlay canvas. If not, let me know.
The reason this works is because a sprite will always be stretched to have the same width and height as the RectTransform of its Image component's GameObject.
I'm trying to match the end of a line drawn with the line renderer to a point in space that is only a few units from the camera and is over an object that could be very far from the camera or very close. When I use WorldToScreenPoint I get results that are incomprehensible to me. Setting position 0 is where I get the incomprehensible results. The code is a component of the object I am trying to get the screen position of. Any help is appreciated.
foreach (int i in identifiers)
{
GameObject lineHolder = new GameObject();
lineHolder.transform.position = transform.position;
lineHolder.transform.parent = transform;
LineRenderer line = lineHolder.AddComponent<LineRenderer>();
lineHolders.Add(lineHolder);
line.positionCount = 2;
line.SetPosition(0, Camera.main.WorldToScreenPoint(new Vector3(transform.position.x, transform.position.y, master.screenPoint.z)));
line.widthMultiplier = .1f;
line.SetPosition(1, master.controlPanel.controlElements[i].position);
}
This is how I get the "screenPoint" in the "master" code I am referring to:
screenPoint = Camera.main.WorldToScreenPoint(controlPanel.transform.position);
The "controlPanel" object is 5 units from the camera. I am trying to keep the lines from being drawn under objects in the control panel, which are just objects and are not part of a canvas, but stay at 5 units from the camera.
I converted from a screen point to a world point. Here is the corrected code that works:
foreach (int i in identifiers)
{
GameObject lineHolder = new GameObject();
lineHolder.transform.position = transform.position;
lineHolder.transform.parent = transform;
LineRenderer line = lineHolder.AddComponent<LineRenderer>();
lineHolders.Add(lineHolder);
line.positionCount = 2;
Vector3 screenPoint = Camera.main.WorldToScreenPoint(transform.position);
line.SetPosition(0, Camera.main.ScreenToWorldPoint(new Vector3(screenPoint.x, screenPoint.y, master.controlPanel.transform.localPosition.z)));
line.widthMultiplier = .1f;
line.SetPosition(1, master.controlPanel.controlElements[i].position);
I am trying to draw a line from one object to a target object. I have managed to do this however the lines are drawn from the centers
What I would like to do is draw the lines from the edge of a plane to the edge of a plane on the target
In this image the white lines are the currently drawn connection and the red lines are how I would like the lines to be drawn
This is how the lines are drawn right now
foreach (GameObject planet in LinkedPlanets)
{
GameObject PlanetLine = new GameObject();
PlanetLine.transform.position = this.transform.position;
PlanetLine.name = this.transform.name + " To " + planet.transform.name;
PlanetLine.transform.parent = this.transform;
PlanetLine.AddComponent<LineRenderer>();
LineRenderer lr = PlanetLine.GetComponent<LineRenderer>();
lr.material = new Material(Shader.Find("Particles/Additive"));
lr.SetWidth(3f, 3f);
lr.SetPosition(0, this.transform.position);
lr.SetPosition(1, planet.transform.position);
lr.GetComponent<Renderer>().material.SetColor("_TintColor", new Color(1, 1, 1, 0.2f));
}
This code is not tested, so may be wrong.
EDIT:
I see that you want to connect from edges of plane, so all change is instead of having direction vector from one planet to other, is to have direction based on longer axis of this vector. If the "x" is longer then "z" use Vector3.right multiplied by unary operator which depends on if the direction vector was below zero or not.
END OF EDIT
You need to know the radius of each circle. Let's say that in this example you know it, and it is 5f and 4f. Then you need to calculate the "meeting" points. You can do it like this:
foreach (GameObject planet in LinkedPlanets)
{
float radiusA = 5f;
float radiusB = 4f;
GameObject PlanetLine = new GameObject();
PlanetLine.transform.position = this.transform.position;
PlanetLine.name = this.transform.name + " To " + planet.transform.name;
PlanetLine.transform.parent = this.transform;
PlanetLine.AddComponent<LineRenderer>();
LineRenderer lr = PlanetLine.GetComponent<LineRenderer>();
lr.material = new Material(Shader.Find("Particles/Additive"));
lr.SetWidth(3f, 3f);
Vector3 pointA = Vector3.ClampMagnitude (planet.transform.position - this.transform.position, radiusA);
Vector3 pointB = Vector3.ClampMagnitude (this.transform.position -planet.transform.position, radiusB);
lr.SetPosition(0, this.transform.position + pointA);
lr.SetPosition(1, planet.transform.position + pointB);
lr.GetComponent<Renderer>().material.SetColor("_TintColor", new Color(1, 1, 1, 0.2f));
}
So let's explain.
First you need to get the direction vector like:
planet.transform.position - this.transform.position
then clamp its length to end on radius length:
Vector3 pointA = Vector3.ClampMagnitude (planet.transform.position - this.transform.position, radiusA);
And finally add this vector to the actuall position:
this.transform.position + pointA
I want to get a Collide Vector of a Mesh using a Up-Vector.
I have a position and a Up Vector.. with these both I calculate the far vector
public Vector3 SetPlayerToGround(Matrix Object, Matrix Player, Mesh GroundObject)
{
Vector3 IntersectVector = new Vector3(0, 0, 0);
if (GroundObject != null)
{
Vector3 Player_UP = new Vector3(Player.M12, Player.M22, Player.M32);
Vector3 PlayerPos = new Vector3(Player.M14, Player.M24, Player.M34);
Vector3 PlayerPos_Far = PlayerPos - Player_UP ;
gameengine.m_Device.Transform.World = Object;
IntersectInformation closestIntersection;
if (GroundObject.Intersect(PlayerPos, PlayerPos_Far, out closestIntersection))
{
IntersectVector = Player_UP + (PlayerPos_Far * closestIntersection.Dist);
}
}
return IntersectVector;
}
Well if I do
Vector3 PlayerPos_Far = PlayerPos + Player_UP;
It will always intersect with nothing...
But the Object which I want to intersect is always under the "position - UpVector"
so I think
Vector3 PlayerPos_Far = PlayerPos - Player_UP ;
is right
why I cant intersect?
Here is a better Description:
Here is the Player and he is into a Ship. the Player is always at 0,0,0 because I move the world around the player. if I Move the Player forward I Chance the playerposition vector which chance only the positions of all other objects.
But I think the player has nothing to do with the Intersect.. but the ship itself.
I use the Position 0,0,0 and the upvector as direction to get the intersectvector of the ground from the ship. the Matrix of the Ship is (Matrix Object):
Vector3 com = ship.position - gamemanager.player.position;
Matrix world;
world = Matrix.Identity * Matrix.Scaling(0.001f, 0.001f, 0.001f) * Matrix.Translation(com);
m_Device.Transform.World = world;
I tried something and I think he wont use my translated Matrix of the ship...
You have to provide a ray defined by a position and a direction. You do not have to specify two points. So pass -PLAYER_UP instead of PlayerPos_Far.
Furthermore, the Intersect method will not care about transformations. You have to pretransform the ray. Transform both the ray position and the direction with the inverse world matrix (transform from world space to model's space) and you should be fine.
Vector3 rayStart = PlayerPos;
Vector3 rayDir = -PLAYER_UP;
Matrix world; //the ship's world matrix
world = Matrix.Invert(matrix);
rayStart = Vector3.TransformCoordinate(rayStart, world); //you may need to add an out parameter
rayDir = Vector3.TransformNormal(rayDir, world);
GroundObject.Intersect(rayStart, rayDir, ...