I have a cylinder that moves with Translate, I need the platform to always be near the center of the cylinder. As in the photo below.
.
To do this, I wrote a script that calculates how far the cylinder has moved, and adds this number to the platform scale and adds this number divided by two to the platform Z position.
if (transform.position.z > spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z)
{
zDistance = transform.position.z - spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z;
spawnedBlocks[spawnedBlocks.Count - 1].transform.position = new Vector3(spawnedBlocks[spawnedBlocks.Count - 1].transform.position.x, spawnedBlocks[spawnedBlocks.Count - 1].transform.position.y, spawnedBlocks[spawnedBlocks.Count - 1].transform.position.z + (zDistance / 2));
spawnedBlocks[spawnedBlocks.Count - 1].transform.localScale = new Vector3(1, 0.1f, spawnedBlocks[spawnedBlocks.Count - 1].transform.localScale.z + zDistance);
}
This code is in my Fixed Update and it turns out that the platform moves much faster and goes forward.
The main mistake you make is in the positioning.
Cleaned up a bit your code looks somewhat like this:
var myPos = transform.position;
var block = spawnedBlocks[spawnedBlocks.Count - 1].transform;
var blockPos = block.position;
if (myPos.z > blockPos.z)
{
zDistance = myPos.z - blockPos.z;
block.position += Vector3.forward * zDistance / 2f;
block.localScale = new Vector3(1, 0.1f, block.z + zDistance);
}
But now have a careful look what you are doing here:
if the myPos.z is bigger than blockPos.z you will move it forward
Now wait a minute! Shouldn't it rather stay at the center between its original spot and the myPos?
Example:
Let's say both objects start at z=0.
Now the myPos moves to z=2 (just example numbers)
zDistance = 2
you move the block to current position + half of distance so 0 + 1
This frame you don't move at all (easier to explain this way)
myPos.z (2) is still bigger than blockPos.z (1)
so distance is now 1
you move the block to current position + half of distance 1 + 0.5
Etc. You see what you are doing is basically interpolating the block position towards the transform.position, not how it should be to the center between the original position and transform.position!
So one solution (I don't see your full code) would be to store the original z position and make sure you are always at the center between that original position and the current transform.position
block.position = originalBlockPosition + Vevtor3.forward * (transform.position - originalBlockPosition).z / 2f;
As mentioned way simpliest though would be to rather just create a parent for your object and make an offset this that the parent is exactly the pivot you want to scale around. Somewhat like
*-ParentPivot
[*****Your Block*****]
This way you can simply scale the parent and the block will always be stretched correctly! You would only have to make sure your block by default has a width of 1 unit the you could directly use something like
parent.localScale = new Vevtor3(1, 1, transform.position.z - parent.position.z);
Related
Can't figure out math formula for changing one value based on another.
enter image description here
I wanna do mechanic where player gets bigger when comes close to bottom border and shrinks when comes close to top. Red is Y coordinates (should not really matter but just to example) and green is desired player scale. So when player at the bottom of the screen he should be 1.25 scale and at the top 0.75.
In course I am doing teacher uses this version (but this one doesn't work for me):
private void AdjustPerspective() {
Vector3 scale = transform.localScale;
scale.x = perspectiveScale * (scaleRatio - transform.position.y);
scale.y = perspectiveScale * (scaleRatio - transform.position.y);
transform.localScale = scale;
}
First line just declare future scale variable.
Second and third changing X and Y of scale (perspectiveScale and scaleRatio float variables which we created ourselves for control and transform.position.y is player's Y position every frame)
Fourth line is assigning new scale.
Can someone please help me with formula for this? Need to update scale value dynamically depending on Y coordinate.
Try this:
float scale = 1 + perspectiveScale * (scaleRatio - transform.position.y);
In your case, with values like:
scaleRatio = 0
perspectiveScale = -0.25 / 4.2
And I recommend you to use intermediary variable, like this:
Vector3 scale = transform.localScale;
float newScale = 1 + perspectiveScale * (scaleRatio - transform.position.y);
scale.x = newScale;
scale.y = newScale;
transform.localScale = scale;
This way, you avoid error if you have to change your formula in the future.
Also, if nothing happens, don't forget to call your method in Update
I'm working on a game for game jam and part of it is to make platforms that move smoothly. They slow down at the ends of their movement before turning around. The platforms simply move side to side or up and down between two waypoints(which are just empty transforms). I have code that uses cosine to determine the speed which works well except it doesn't align with the waypoints, the platforms tend to slow and change direction before ever reaching the waypoints. I need a way to use the distance between the waypoints as a variable in determining how the cosine equation changes speed so that the platforms slow and reverse direction exactly at the waypoints.
Here is what I have so far:
void Side_to_side()
{
if (waypointIndex < horWaypoints.Length)
{
platformSpeed = (1f * (float)Mathf.Cos(2f * (float)Mathf.PI * 1f * totalTime));
Vector3 targetPosition = horWaypoints[waypointIndex].position;
float delta = platformSpeed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, targetPosition, delta);
if (transform.position.x == targetPosition.x && transform.position.y == targetPosition.y)
{
if (waypointIndex + 1 == horWaypoints.Length)
waypointIndex = 0;
else
waypointIndex++;
}
}
else
{
waypointIndex = 0;
}
//Translate platform back and forth between two waypoints
}
As I have said this code moves the platforms in the motions i want but they don't use the waypoints as turn around points. I understand I could do away with the waypoints and just calculate how far I would like each platform to go before turning around individually but that would take time to do it for each platform whereas I'd like to quickly put down waypoint pairs for them to use and the script calculates what the perfect values would be to match the waypoint locations.
If I understand you correctly you want to move an object forth and back between exactly two positions and apply some smoothing to the movement.
I would rather use a combination of Vector2.Lerp with a Mathf.PingPong as factor and you can then apply ease in and out using additionally Mathf.SmoothStep.
This could look like e.g.
public Transform startPoint;
public Transform endPoint;
// Desired duration in seconds to go exactly one loop startPoint -> endPoint -> startPoint
public float duration = 1f;
private void Update ()
{
// This will move forth and back between 0 and 1 within "duration" seconds
var factor = Mathf.PingPong(Time.time / (2f * duration), 1f);
// This adds additional ease-in and -out near to 0 and 1
factor = Mathf.SmoothStep(0, 1, factor);
// This interpolates between the given positions according to the given factor
transform.position = Vector2.Lerp(startPoint, endPoint, factor);
}
you could of course still use cosine if necessary, basically any function that returns a value between 0 and 1. You just have to use the correct multiplier in order to achieve the desired duration in seconds.
Note: Typed on the phone and not 100% sure on the math but I hope the idea gets clear
So, here is what i have:
SteamVR's hand gameobject
3D sphere.
what i want:
The sphere to move to same direction/position as the hand does, but it to move further with a multiplier. E.g. i move the VR controller and the hand moves 1 unit. I want that the sphere moves to the same direction in a same amount of time but e.g. 2 units. how do i do this?
i tried simple
sphere.transform.position = ControllerToFollow.position +2f;
but then the sphere is always offset.
position is a Vector3, which is essentially 3 floats - you can't plus a Vector3 with a float unless you overload the + operator. Otherwise what you can do is the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f,
ControllerToFollow.position.y + 2f,
ControllerToFollow.position.z + 2f);
sphere.transform.position = followPos;
If you only want it to follow on one axis, then you can do the following:
Vector3 followPos = new Vector3(ControllerToFollow.position.x + 2f, // Follow on x Axis
ControllerToFollow.position.y, // Y axis is the same
ControllerToFollow.position.z); // X Axis is the same
sphere.transform.position = followPos;
Edit: I think I understand your problem better now. Here's a better version.
if (Vector3.Distance(sphere.transform.position, ControllerToFollow.position) >= 2f)
{
// Code that makes the sphere follow the controlling
}
Just track the movement Delta of the hand and multiply it by a certain multiplier.
At the beginning of the manipulation store
private Vector3 lastControllerPosition;
...
lastControllerPosition = ControllerToFollow.position;
then in every frame compare
var delta = ControllerToFollow.position - lastHandPosition;
// Don't forget to update lastControllerPosition for the next frame
lastControllerPosition = ControllerToFollow.position;
Now in delta you have a movement of the controller since the last frame. So you can assign it to the sphere with a multiplier using Transform.Translate
sphere.transform.Translate(delta * multiplier, Space.World);
or simply using
sphere.transform.position += delta * multiplier;
I have a Vector2-variable where vector.x and vector.y have been clamped so that both are only allowed a value between 0 and 4. Obviously, this leaves me to having a quadratic space around the coordinate 2, 2 to move a gameobject around on.
But what I want to achieve is having a circular space to move a gameobject on, which means that I need it to clamp the value of vector.x+vector.y to be a total value in between 0 and 4, but I can't seem to make it work. Is there another function I should use instead of clamp?
This is what I have at the moment:
Vector2 pos = rigidbody2D.transform.position;
pos.x = Mathf.Clamp (pos.x, 19.5f, 24);
pos.y = Mathf.Clamp (pos.y, 3.3f, 6);
rigidbody2D.transform.position = pos;
Instead of clamping each axis individually, how can I give them a total minimum- and maximum-value?
There is a similar function for Vectors: Vector2.ClampMagnitude and Vector3.ClampMagnitude
You can only specify a maximum length, so you have to take care of the minimum yourself. The problem with the minimum value is, that the function would not know what to do if the vector has a length of 0. In which direction should the vector point to achieve the minimum length?
Limit input to a circle
If you only want to limit the movement to a circle, you don't need a minimum value. Instead define the center as (2,2) and limit the movement to a radius of 2.
Vector2 center = new Vector2(2f, 2f);
Vector2 moveBy = new Vector(4f, 7f); // set this to whatever your input is
moveBy = Vector2.ClampMagnitude(moveBy, 2f);
Vector2 newPosition = center + moveBy;
newPosition will be inside a circle with a radius of 2 around your center at (2,2)
Limit given position to a circle
If you want to clamp a given position to a circle, you can slightly modify the version above. It's like putting the object on a leash and pull it back when it leaves the circle.
Vector2 center = new Vector2(2f, 2f);
Vector2 position = new Vector2(6f, 5f); // outside your desired circle
Vector2 offset = position - center;
Vector2.ClampMagnitude(offset, 2f);
position = center + offset;
Unfortunately There is no in-built function available same as clamp function. but if you don't want to use CLAMP function then you can use IF statements.
if(pos.x > 19.5f )
{
//put logic here
}
if( pos.x < 24f)
{
//put logic here
}
if(pos.y > 3.3f)
{
//put logic here
}
if( pos.y < 6f)
{
//put logic here
}
you can do this. but in my opinion Mathf.Clamp() is the best option for Restricting values in unity,if conditions will not be accurate as Mathf.Clamp() Function.
Not C#, but here's a snippet I use for limiting the touchpad knob to the area of the touchpad in a libgdx project of mine:
Vector2 maxPos = new Vector2(screenX, screenY);
if (maxPos.dst(new Vector2(Settings.touchpadPositionX, Settings.touchpadPositionY)) > painter.getTouchpadSize())
{
maxPos.sub(Settings.touchpadPositionX,Settings.touchpadPositionY);
maxPos.nor();
maxPos.scl(painter.getTouchpadSize());
maxPos.add(Settings.touchpadPositionX,Settings.touchpadPositionY);
}
painter.updateTouchpadPos((int)maxPos.x, (int)maxPos.y);
I am encountering an issue when updating a bullet's position when I rotate the ship that shoots it. Currently the code almost works except that at most angles the bullet is not fired from where i want it to. Sometimes it fires slightly upward than the centre where it should be, and sometimes downwards. I'm pretty sure it has something to do with the trigonometry but I'm having a hard time finding the problem.
This code is in the constructor; it sets the bullet's initial position and rotation based on the ship's position (sp.position) and its rotation (Controls.rotation):
this.backgroundRect = new RotatedRectangle(Rectangle.Empty, Controls.rotation);
//get centre of ship
this.position = new Vector2(sp.getPosition().X + sp.getTexture().Width/2,
(sp.getPosition().Y + sp.getTexture().Height / 2));
//get blast pos
this.position = new Vector2((float)(this.position.X + (sp.getTexture().Width / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(sp.getTexture().Width / 2 * Math.Sin(this.backgroundRect.Rotation))));
//get blast pos + texture height / 2
this.position = new Vector2((float)(this.position.X + (this.texture.Height / 2 *
Math.Cos(this.backgroundRect.Rotation))), (float)(this.position.Y +
(this.texture.Height / 2 * Math.Sin(this.backgroundRect.Rotation))));
this.backgroundRect = new RotatedRectangle(new Rectangle((int)this.position.X,
(int)this.position.Y, this.texture.Width, this.texture.Height), Controls.rotation);
speed = defSpeed;
Then in the update method I use the following code; I'm pretty sure this is working fine though:
this.position.X = this.position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation);
this.position.Y = this.position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation);
this.getRectangle().CollisionRectangle.X = (int)(position.X + defSpeed *
(float)Math.Cos(this.getRectangle().Rotation));
this.getRectangle().CollisionRectangle.Y = (int)(position.Y + defSpeed *
(float)Math.Sin(this.getRectangle().Rotation));
Also I should mention that my ship had not been working correctly when I rotated it since the origin (center of rotation) was (0,0). To remedy this I changed the origin to the center of the ship (width/2,height/2) and also added those values to the drawing rectangle so that the sprite would draw correctly. I imagine this could be the problem and suppose there's a better way of going around this. The game is in 2D by the way.
The problem is that the sprites are not drawn on the positions they are supposed to be. When calculating the center position, you assume that the sprite is not rotated. This causes some trouble. Furthermore, rotating an arbitrary vector is not as easy as you did. You can only rotate a vector by multiplying its components with sin/cos when it is on the x-axis.
Here is how you can rotate arbitrary vectors:
Vector2 vecRotate(Vector2 vec, double phi)
{
double length = vec.Length(); //Save length of vector
vec.Normalize();
double alpha = Math.Acos(vec.X); //Angle of vector against x-axis
if(vec.Y < 0)
alpha = 2 * Math.PI - alpha
alpha += phi;
vec.X = Math.Cos(alpha) * length;
vec.Y = Math.Sin(alpha) * length;
return vec;
}
Make sure, the angle is in radians.
With that you can calculate the correct position of the ship:
Vector2 ShipCenter = sp.Position() + vecRotate(new Vector2(sp.getTexture().Width/2, sp.getTexture().Height/2), Controls.Rotation);
You can use the same function to determine the position of the bullet's center. I don't exactly know, where you want to place the bullet. I will assume, it is at the center of the right edge:
Vector2 offset = new Vector2(sp.getTexture.Width/2, 0);
this.position = ShipCenter + vecRotate(offset, Controls.Rotation);
If you then need the position of the upper left corner of the bullet, you can go further:
this.position -= vecRotate(new Vector2(this.texture.Width/2, this.texture.Height/2), Controls.Rotation)
However, as I stated, it is probably easier to handle the ship's and bullets' positions as central positions. Doing so, you need far less trigonometry.