Unity 5.4.2 - Scale and position instantiated UI elements - c#

So I've been working on a project for a class I'm in and have run into a pickle that I can't seem to resolve. I have asked the instructor and he was confused as I was.
Right now I've just been trying to set up a simple UI for displaying a list of buttons to select a level to load. At runtime the code simply creates the appropriate buttons and such, and places them within a scrollview's content object. Simple enough, right? Well, as you can see the position and right are completely wrong, by 800 units.
The Canvas is set to a Screen Space - Overlay and the Canvas Scaler is set to scale with screen size, which I'm guessing is the problem. I already had the problem of the position and scale being even more off (depending on the screen size), but "fixed" this by manually setting the transform's position and localscale.
I have however had no luck finding a solution to this, with all my googling, so I'd be very appreciative if someone could help me with this and explain to me what the problem is.
Here is the code that it executes for each of the levels. It's of course messy because I've been trying about as much as I could to figure out what the problem is.
Level thisLevel = levelManager.levelList [i];
GameObject levelButton = (GameObject)Instantiate (menuManager.levelButtonContainerObject);
levelButton.transform.SetParent (menuManager.mainMenuLevelContainer.transform, false);
levelButton.transform.position = Vector3.zero;
levelButton.transform.localPosition = Vector3.zero;
levelButton.transform.localScale = Vector2.one;
RectTransform levelButtonRect = levelButton.GetComponent<RectTransform> ();
// levelButtonRect.position = ApplyCanvasScale (new Vector3 (0f, -50 + (-100 * i), 0f));
levelButtonRect.position = new Vector3(0f, -50 + (-100 * i), 0f);
// levelButtonRect.position = Vector3.zero;
levelButtonRect.sizeDelta = new Vector2 (0f, 100f);
Debug.Log ("Position " + levelButtonRect.position + ", Scale " + levelButtonRect.sizeDelta);

Try adding these where you're setting position and scale:
levelButton.transform.offsetMin = Vector2.zero
levelButton.transform.offsetMax = Vector2.zero

Related

Unity: How can I put one object on top of another

I need put my 3D Object dynamicly to platform because 3D Object has a different size.
My code is _sceneObject.sceneItem.transform.localPosition = _gobletPlatform.localPosition; but he fixes on it, not on top of it.
Collider platformCollider = (_gobletPlatform.GetComponent<Collider>();
Vector3 pos = new Vector3 (_gobletPlatform.transform.position.x + platformCollider.bounds.size.x, _gobletPlatform.transform.position.y + platformCollider.bounds.size.y, _gobletPlatform.transform.position.z + platformCollider.bounds.size.z);
_sceneObject.sceneItem.transform.position = pos;
Note: I can't test this now. But, I hope it works.

How to make my sprite object go offscreen to the left and have it appear on the right?

In Unity, I'm trying to make my sprite go offscreen to the left and have it appear offscreen on the right or vice versa. I have my sprite move left or right (depending on user input) constantly for my gameplay by the way.
I take into account that the sprite needs to be fully offscreen before having it appear on the right side.
Here's my current code:
void Start ()
{
minXValueWorld = Camera.main.ScreenToWorldPoint(new Vector3(0,0,0)).x;
maxXValueWorld = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0)).x;
playerSize = this.GetComponent<Renderer>().bounds.size;
}
void Move()
{
if (inputLeft)
{
this.transform.position -= new Vector3(speed * Time.deltaTime, 0, 0);
}
else
{
this.transform.position += new Vector3(speed * Time.deltaTime, 0, 0);
}
}
void OffscreenCheck()
{
Vector3 screenPos = Camera.main.WorldToScreenPoint(this.transform.position);
Vector3 maxWorldXWithPlayerSize = Camera.main.WorldToScreenPoint(new Vector3(maxXValueWorld,0,0) + playerSize/2);
Vector3 minWorldWithPlayerSize = Camera.main.WorldToScreenPoint(new Vector3(minXValueWorld,0,0) - playerSize/2);
if (screenPos.x < minWorldWithPlayerSize.x)
{
this.transform.position = new Vector3(maxWorldXWithPlayerSize.x, this.transform.position.y, this.transform.position.z);
}
if (screenPos.x > maxWorldXWithPlayerSize.x)
{
this.transform.position = new Vector3(minWorldWithPlayerSize.x, this.transform.position.y, this.transform.position.z);
}
}
Then the Move and OffscreenCheck are called in the Update function in order.
The problem with this code is that once my sprite goes fully offscreen on the left, it appears on the right offscreen, but it does not move left anymore.
Instead, it just teleports to the left or right offscreen positions. I do not see it move left or right across the screen anymore because of this.
I'm pretty sure that my code logic is just off. Does anyone know how to fix this issue?
Thanks
I think the following should work:
void OffscreenCheck()
{
Vector3 screenPos = Camera.main.WorldToScreenPoint(this.transform.position);
Vector3 maxWorldWithPlayerSize = new Vector3(maxXValueWorld + playerSize/2,0,0);
Vector3 minWorldWithPlayerSize = new Vector3(minXValueWorld - playerSize/2,0,0);
Vector3 maxScreenWithPlayerSize = Camera.main.WorldToScreenPoint(maxWorldWithPlayerSize);
Vector3 minScreenWithPlayerSize = Camera.main.WorldToScreenPoint(minWorldWithPlayerSize);
if (screenPos.x < minScreenWithPlayerSize.x)
{
this.transform.position = new Vector3(
maxWorldWithPlayerSize.x,
this.transform.position.y,
this.transform.position.z);
}
if (screenPos.x > maxScreenWithPlayerSize.x)
{
this.transform.position = new Vector3(
minWorldWithPlayerSize.x,
this.transform.position.y,
this.transform.position.z);
}
}
The above is a good illustration of why to be careful about how you name your variables. When I first looked at your code, I failed to connect the fact that while the variables had the word "World" in them, they were actually in screen coordinates. So I didn't notice the faulty assignment of the position, in which you used screen coordinates to assign an X coordinate in world coordinates.
In the above, I have separated the world and screen coordinate vectors into individual local variables. The screen coordinates are used for the off-screen comparison itself, while the world coordinates are used for actually moving the sprite to where you want.
Again, lacking a complete code example I'm not able to actually test the above and verify that it solves your problem. But I think it will.

Having the Background or Camera "Scroll" based on charcter position

I'm working on an RPG game that has a Top-Down view. I want to load a picture into the background which is what the character is walking on, but so far I haven't figured out how to correctly have the background redraw so that it's "scrolling". Most of the examples I find are auto scrolling.
I want the camera to remained centered at the character until you the background image reaches its boundaries, then the character will move without the image re-drawing in another position.
Your question is a bit unclear, but I think I get the gist of it. Let's look at your requirements.
You have an overhead camera that's looking directly down onto a two-dimensional plane. We can represent this as a simple {x, y} coordinate pair, corresponding to the point on the plane at which the camera is looking.
The camera can track the movement of some object, probably the player, but more generally anything within the game world.
The camera must remain within the finite bounds of the game world.
Which is simple enough to implement. In broad terms, somewhere inside your Update() method you need to carry out steps to fulfill each of those requirements:
if (cameraTarget != null)
{
camera.Position = cameraTarget.Position;
ClampCameraToWorldBounds();
}
In other words: if we have a target object, lock our position to its position; but make sure that we don't go out of bounds.
ClampCameraToBounds() is also simple to implement. Assuming that you have some object, world, which contains a Bounds property that represents the world's extent in pixels:
private void ClampCameraToWorldBounds()
{
var screenWidth = graphicsDevice.PresentationParameters.BackBufferWidth;
var screenHeight = graphicsDevice.PresentationParameters.BackBufferHeight;
var minimumX = (screenWidth / 2);
var minimumY = (screnHeight / 2);
var maximumX = world.Bounds.Width - (screenWidth / 2);
var maximumY = world.Bounds.Height - (screenHeight / 2);
var maximumPos = new Vector2(maximumX, maximumY);
camera.Position = Vector2.Clamp(camera.Position, minimumPos, maximumPos);
}
This makes sure that the camera is never closer than half of a screen to the edge of the world. Why half a screen? Because we've defined the camera's {x, y} as the point that the camera is looking at, which means that it should always be centered on the screen.
This should give you a camera with the behavior that you specified in your question. From here, it's just a matter of implementing your terrain renderer such that your background is drawn relative to the {x, y} coordinate specified by the camera object.
Given an object's position in game-world coordinates, we can translate that position into camera space:
var worldPosition = new Vector2(x, y);
var cameraSpace = camera.Position - world.Postion;
And then from camera space into screen space:
var screenSpaceX = (screenWidth / 2) - cameraSpace.X;
var screenSpaceY = (screenHeight / 2) - cameraSpace.Y;
You can then use an object's screen space coordinates to render it.
Your can represent the position in a simple Vector2 and move it towards any entity.
public Vector2 cameraPosition;
When you load your level, you will need to set the camera position to your player (Or the object it should be at)
You will need a matrix and some other stuff, As seen in the code below. It is explained in the comments. Doing it this way will prevent you from having to add cameraPosition to everything you draw.
//This will move our camera
ScrollCamera(spriteBatch.GraphicsDevice.Viewport);
//We now must get the center of the screen
Vector2 Origin = new Vector2(spriteBatch.GraphicsDevice.Viewport.Width / 2.0f, spriteBatch.GraphicsDevice.Viewport.Height / 2.0f);
//Now the matrix, It will hold the position, and Rotation/Zoom for advanced features
Matrix cameraTransform = Matrix.CreateTranslation(new Vector3(-cameraPosition, 0.0f)) *
Matrix.CreateTranslation(new Vector3(-Origin, 0.0f)) *
Matrix.CreateRotationZ(rot) * //Add Rotation
Matrix.CreateScale(zoom, zoom, 1) * //Add Zoom
Matrix.CreateTranslation(new Vector3(Origin, 0.0f)); //Add Origin
//Now we can start to draw with our camera, using the Matrix overload
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
RasterizerState.CullCounterClockwise, null, cameraTransform);
DrawTiles(spriteBatch); //Or whatever method you have for drawing tiles
spriteBatch.End(); //End the camera spritebatch
// After this you can make another spritebatch without a camera to draw UI and things that will not move
I added the zoom and rotation if you want to add anything fancy, Just replace the variables.
That should get you started on it.
However, You will want to make sure the camera is in bounds, and make it follow.
Ill show you how to add smooth scrolling, However if you want simple scrolling see this sample.
private void ScrollCamera(Viewport viewport)
{
//Add to the camera positon, So we can see the origin
cameraPosition.X = cameraPosition.X + (viewport.Width / 2);
cameraPosition.Y = cameraPosition.Y + (viewport.Height / 2);
//Smoothly move the camera towards the player
cameraPosition.X = MathHelper.Lerp(cameraPosition.X , Player.Position.X, 0.1f);
cameraPosition.Y = MathHelper.Lerp(cameraPosition.Y, Player.Position.Y, 0.1f);
//Undo the origin because it will be calculated with the Matrix (I know this isnt the best way but its what I had real quick)
cameraPosition.X = cameraPosition.X -( viewport.Width / 2);
cameraPosition.Y = cameraPosition.Y - (viewport.Height / 2);
//Shake the camera, Use the mouse to scroll or anything like that, add it here (Ex, Earthquakes)
//Round it, So it dosent try to draw in between 2 pixels
cameraPosition.Y= (float)Math.Round(cameraPosition.Y);
cameraPosition.X = (float)Math.Round(cameraPosition.X);
//Clamp it off, So it stops scrolling near the edges
cameraPosition.X = MathHelper.Clamp(cameraPosition.X, 1f, Width * Tile.Width);
cameraPosition.Y = MathHelper.Clamp(cameraPosition.Y, 1f, Height * Tile.Height);
}
Hope this helps!

C# XNA Mouse position projected to 3D plane

I'm working on a 3D XNA project, and I've been thinking about this problem for like 2 weeks.
So I just decided to ask you.
Basically I have a flat plane and i want to project the mouse position to that plane, but how?
I tried many ways to do it, calculated angles...
But i figured out, that the distance must effect on the X position, maybe some math is needed what I've never heard before.
I did some code few years ago which returns the position as Vector3(x,y,z), given mouse state:
private Vector3 FindWhereClicked(MouseState ms)
{
Vector3 nearScreenPoint = new Vector3(ms.X, ms.Y, 0);
Vector3 farScreenPoint = new Vector3(ms.X, ms.Y, 1);
Vector3 nearWorldPoint = device.Viewport.Unproject(nearScreenPoint, cam.projectionMatrix, cam.viewMatrix, Matrix.Identity);
Vector3 farWorldPoint = device.Viewport.Unproject(farScreenPoint, cam.projectionMatrix, cam.viewMatrix, Matrix.Identity);
Vector3 direction = farWorldPoint - nearWorldPoint;
float zFactor = -nearWorldPoint.Y / direction.Y;
Vector3 zeroWorldPoint = nearWorldPoint + direction * zFactor;
return zeroWorldPoint;
}
device is an instance of GraphicsDevice.
Hope it works for you.

Camera flip problem

I'm programming a game in C# using the XNA3.1 engine. However I'm having a small issue with my camera, basically my camera tends to "flip" when it rotates more than 180 degrees on its roll (when the camera reaches 180 degrees, it seems to flip back to 0 degrees). The code for obtaining the view matrix is as follows:
Globals.g_GameProcessingInfo.camera.viewMat = Matrix.CreateLookAt(Globals.g_GameProcessingInfo.camera.target.pos, Globals.g_GameProcessingInfo.camera.LookAt, up); //Calculate the view matrix
The Globals.g_GameProcessingInfo.camera.LookAt variable the position 1 unit directly in front of the camera, relative to the rotation of the camera, and the "up" variable is obtained with the following function:
static Vector3 GetUp() //Get the up Vector of the camera
{
Vector3 up = Vector3.Zero;
Quaternion quat = Quaternion.Identity;
Quaternion.CreateFromYawPitchRoll(Globals.g_GameProcessingInfo.camera.target.rot.Y, Globals.g_GameProcessingInfo.camera.target.rot.X, Globals.g_GameProcessingInfo.camera.target.rot.Z, out quat);
up.X = 2 * quat.X * quat.Y - 2 * quat.W * quat.Z; //Set the up x-value based on the orientation of the camera
up.Y = 1 - 2 * quat.X * quat.Z - 2 * quat.Z * quat.Z; //Set the up y-value based on the orientation of the camera
up.Z = 2 * quat.Z * quat.Y + 2 * quat.W * quat.X; //Set the up z-value based on the orientation of the camera
return up; //Return the up Vector3
}
I got same problems in OpenGL with gluLookAt. I fixed that problem with my own camera class:
void Camera::ComputeVectors()
{
Matrix4x4 rotX, rotZ;
Quaternion q_x, q_y, q_z;
Quaternion q_yx, q_yz;
q_x.FromAngleAxis(radians.x, startAxisX);
q_y.FromAngleAxis(radians.y, startAxisY);
q_z.FromAngleAxis(radians.z, startAxisZ);
q_yx = q_y * q_x;
q_yx.ToMatrix(rotZ);
q_yz = q_y * q_z;
q_yz.ToMatrix(rotX);
axisX = startAxisX;
axisZ = startAxisZ;
axisX.Transform(rotX);
axisZ.Transform(rotZ);
axisY = axisX.Cross(axisZ);
position = startPosition;
position -= center;
position.Transform(q_yx);
position += center;
}
It is maybe overcomplicated, but working. axisY is your up vector.
Full code listing is at:
http://github.com/filipkunc/opengl-editor-cocoa/blob/master/PureCpp/MathCore/Camera.cpp
Hope it helps.
This is probably slower but the only way I know to do with would be the with the rotation matrix for 3D. Wikipedia Link
Where
and U = (Camera.position - Camera.lookat).norm
... Now, I believe that would give you the rotation part of the view matrix. However, I'm not 100% on it. I'm still looking into this though.
meh was hoping to see a tan in there somewhere.
can you link to where you got your equation from please?
(am at work and really don;t want to sit down myself and derive it)
how are you setting your camera rotation? are you sure nothing is going on there?
I'm a bit unsure about the math in your GetUp method. Could you elaborate on the math behind it?
In my lookat camera I initialize my up-vector once and then rotate that vector using a quaternion. This removes the possibility of trying to do a cross-product on parallel vectors in order to calculate the up vector.
Some semicode to clarify perhaps:
var up = Vector3.Up;
var target = <some point in space>;
var rotation = <current rotation quaternion>;
var forward = target - position;
forward = Vector3.Transform(forward, rotation);
var updatedPosition = target - forward;
var updatedUp = Vector3.Transform(up, rotation);
var view = Matrix.CreateLookAt(updatedPosition, target, updatedUp);
Since I wasn't satisfied with the answers here already, I had to figure this out myself.
What I discovered is it's actually quite simple. Just do this:
Matrix ypr = Matrix.CreateFromYawPitchRoll(yaw, pitch, roll);
Vector3 up = Vector3.Transform(Vector3.Up, ypr);
"up" is the direction you want.

Categories