Camera is not centering between 2 gameobjects correctly - c#

I have a button that makes the MainCamera switch between being in the middle of several objects.
For example, there's the [StartPoint , CheckPoint , CheckPoint , EndPoint]
I want the camera to switch continuously across the checkpoints like the comma between the checkpoints. ^^
This code is the actual switching the position placing for the MainCamera (where the error might be)
public void SwitchCameraBetween(GameObject nextPoint, GameObject afterNextPoint)
{
Vector3 centerPoint = (afterNextPoint.transform.position - nextPoint.transform.position) / 2;
centerPoint.z = -10;
float cameraSize = (afterNextPoint.transform.position - nextPoint.transform.position).magnitude - nextPoint.transform.localScale.x;
mainCamera.transform.position = centerPoint;
mainCamera.orthographicSize = cameraSize / 3;
}
The camera switches perfect for the first 2 checkpoints (either StartPoint & EndPoint or StartPoint & CheckPoint) after the camera stays in same position or off by a couple values. I checked by using Debug.Log(); to see if the camera has the correct gameobjects between and it does so why does it not work?

The center point should be the average of all points, so you need to add your points, not subtract them, and then divide the result by the number of points included for the average.
center = sum(points) / count(points), so center = (a + b) / 2
Alternatively, you could use Vector3.Lerp(a, b, 0.5f) if you find that more readable, although technically this would be slower, since it's both a method call and more operations, unless the compiler is doing fancy things behind the scenes...

Related

How can I ensure that an object using a cosine equation to determine speed meets its lowest valley at the same time it hits a waypoint?

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

Changing the Scale of an object in one direction only

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);

Unity - How to instantiate prefab at slider`s specific value

I`m making a 2d game based on levels in which, each level, you have three checkpoints in the score tracking bar. The player must reach the lowest checkpoint to be able to pass to the next level, but will get a bonus if he reaches the 2nd and 3rd checkpoints.
I tought on using a Slider as the scoring bar. My question is:
Is there a way to store a specific value of the Slider's bar in the Start method and Instantiate a marker prefab at that position? Here's an example:
The Max Value of the Slider at Level 1 is 100.
I want to Instantiate the first marker, with some padding in the y, in the 50`s position of the slider, the second in the 75 position and the third in the 100 position.
My current logic is that I need to, somehow, get the value I want and find his Transform, but I can`t find a way to code this, I have no idea how to get the position I want.
Here are some images to illustrate what i`m trying to do:
i would get the width attribute of the slider, then divide that by sliderMax, the result will be the the width of a single % on the slider. you can then add or subract multiple of this to get a percentages place on the bar.
example: slider.x=50
slider.width=200;
increment = slider.width/100; //this will result in two, giving you two pixels per percent.
so your 50 percent placement would be: sliderx+(increment*50);
keep in mind this is all pseudo code, designed to give you an idea of how to acheive your desired result
I found the solution!
Based on the insights i managed to do this:
void SpawnCheckPoint() {
mySlider.maxValue = gameLevel[currentLevel].maxValue; //Set the slider's Max Value to the max value of the level.
float sliderMaxValue = mySlider.maxValue;
float sliderWidth = slider.GetComponent<RectTransform>().sizeDelta.x; //Get the width of the Slider.
float zeroValue = slider.transform.position.x - (sliderWidth / 2); //Get the leftmost corner of the slider.
//Loop to Instantiate the 3 checkpoints.
for (int i = 0; i < gameLevel[currentLevel].values.Length; i++) {
float valueToIncrement = (gameLevel[currentLevel].values[i] / sliderMaxValue); //Get the % of the checkpoint based on the max value of the level.
float newPos = (sliderWidth * valueToIncrement); //New position in screen
//Instantiate the object as a child of the Slider
GameObject checkpoint = Instantiate(checkPoint, new Vector3(zeroValue + newPos - xPadding, slider.transform.position.y - yPadding, slider.transform.position.z),
Quaternion.identity, GameObject.FindGameObjectWithTag("Slider").transform);
}
}
It's probably not the best way to do what i want but it's working just fine.
Thank you all who tried to help me, your insights were very useful.

Lerp alpha based on distance between 2 objects

So I am trying to increase an image alpha channel based on the fact that an object is getting closer and closer to the player. I am using Vector3.Distance()
to get the distance from the player to the object but I don't know how should I convert the distance so that the value of color.a will get bigger and bigger as the distance get's smaller and smaller.
Please point me in the right direction;
How can I make a number bigger based on the fact that another number is getting smaller?
See this post that explains how to lerp a color based on distance between two GameObjects. The only difference is that you want to lerp the alpha instead so everything written on that post should still be relevant to this. Just few modifications need to be made.
You just need to use Mathf.Lerp instead of Color.Lerp. Also, you need to enable fade mode on the material. You can do that from the Editor or script. The code below is a modified code from the linked answer that should accomplish what you are doing. It also enables fade mode from code in the Start function.
public GameObject obj1;
public GameObject obj2;
const float MAX_DISTANCE = 200;
Renderer mRenderer;
void Start()
{
mRenderer = GetComponent<Renderer>();
//ENABLE FADE Mode on the material if not done already
mRenderer.material.SetFloat("_Mode", 2);
mRenderer.material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
mRenderer.material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
mRenderer.material.SetInt("_ZWrite", 0);
mRenderer.material.DisableKeyword("_ALPHATEST_ON");
mRenderer.material.EnableKeyword("_ALPHABLEND_ON");
mRenderer.material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mRenderer.material.renderQueue = 3000;
}
void Update()
{
//Get distance between those two Objects
float distanceApart = getSqrDistance(obj1.transform.position, obj2.transform.position);
UnityEngine.Debug.Log(getSqrDistance(obj1.transform.position, obj2.transform.position));
//Convert 0 and 200 distance range to 0f and 1f range
float lerp = mapValue(distanceApart, 0, MAX_DISTANCE, 0f, 1f);
//Lerp Alpha between near and far color
Color lerpColor = mRenderer.material.color;
lerpColor.a = Mathf.Lerp(1, 0, lerp);
mRenderer.material.color = lerpColor;
}
public float getSqrDistance(Vector3 v1, Vector3 v2)
{
return (v1 - v2).sqrMagnitude;
}
float mapValue(float mainValue, float inValueMin, float inValueMax, float outValueMin, float outValueMax)
{
return (mainValue - inValueMin) * (outValueMax - outValueMin) / (inValueMax - inValueMin) + outValueMin;
}
I don't know how you want the effect to look, but it's basically a math question.
Something like:
color.a = 1.0 / distance
should get you started. Basically, the further from 1 distance gets (increasing), the closer to 0 color.a gets (decreasing). The opposite is thus true if distance decreases (color.a then increases).
You have to deal with values for distance getting inferior to 1 (if applicable), as it won't increase color.a anymore.
You also have to deal with color.a max possible value: is it 1.0 or 255 (or something else)? Replace the 1.0 in the formula with this max value. You may have to multiply distance by an arbitrary value so the effect isn't too fast or too slow.
Sounds like you want a function (in the mathematical sense) f(x) that maps an input (distance) value in the domain [0, infinity) to the output (alpha) range [1, 0]. One simple such function is 1/(1+x) (click the link to see an interactive graph).
You can use the interactive graph and online math resources to play with the equation to find one that looks good to you. Once you have that figured out, implementing it in code should be easy!

Contrain camera to rectangle while tracking multiple objects

I'm making a 2D platformer that features a dynamic camera. The camera must track 4 players at once so that they're all on the screen. In addition the camera must not move beyond a predefined rectangle boundary. I've tried implementing it but I just can't seem to get the process of zooming the camera so that it's always close as possible to the four objects.
The general algorithm I have so far is
1. Define the viewing space by calculating a 2D axis aligned bounding box using the 4 object positions being tracked and use its center as a camera postion (or averaging)
2. Calculate an orthographic size by using the largest x OR y value using a vector from the camera's position to each object being tracked.
If the camera is beyond the camera's boundary calculate the excess amount and displace in the opposite direction.
This seems simple enough on paper but I can't seem to get a correct working implementation.
Why dont you just take the Average of the 4 players Position and use it as Camera Position, also check if the players are out of boundary and when they are, zoom out.
float x = 0;
float y = 0;
GameObject[] players = new GameObjects[5];
foreach(GameObject _ply in players)
{
x += _ply.transform.position.x;
y += _ply.transform.position.y;
}
x = x/players.Length;
y = y/players.Length;
foreach(GameObject _ply in players)
{
if(_ply.transform.position.x > (x + (Screen.Width / 2)))
//zoom out
if(_ply.transform.position.y > (y + (Screen.Height / 2)))
//zoom out
}
But you have to fix Zoomin.

Categories