0
\$\begingroup\$

I have an Isometric 2D grid-based tilemap, and am firing a projectile (at Vector P1 in image below) from the player (at Vector A) to a location "above" a target location (at Vector B). This works mostly fine (see animation gif at bottom of post).

How can I calculate the location of a shadow sprite? I've tried several usages of dot products and projections but I can't figure out what I am doing wrong. How do I project a location from the projectile arc position P1 down onto the AB line?

unity vectors

The shadow should always be located along the line AB:

line for shadow

This is the closest I've gotten in the below animation:

visual

Here is some of the code for the above animation:

private void Update() { // ... other code for moving the flare sprite var A = new Vector2(_castFromLocation.x, _castFromLocation.y); var B = new Vector2(_hoverOverLocation.x, _hoverOverLocation.y); var P1 = new Vector2(_flare.transform.position.x, _flare.transform.position.y); var AB_ = B - A; var AP1_ = P1 - A; var dotProduct = Vector2.Dot(AB_, AP1_); var distanceAlongAB = dotProduct / AP1_.magnitude; _shadow.transform.position = Vector3.Lerp(_castFromLocation, _hoverOverLocation, distanceAlongAB); } 
\$\endgroup\$
2
  • 2
    \$\begingroup\$ You might find this Q&A on StackOverflow useful here. \$\endgroup\$ Commented Jun 20, 2023 at 16:54
  • \$\begingroup\$ That's definitely helpful, thank you! Though it uses a different physics approach modeling velocity/acceleration. Still hoping if I can get a similar effect similar using projection of position-vectors, and understand the linear algebra there a little better. \$\endgroup\$ Commented Jun 20, 2023 at 17:28

2 Answers 2

0
\$\begingroup\$

Here's a simple, scalable solution. There are several prerequisites to be clarified:

  1. For the oblique throwing motion, it can be decomposed into the uniform linear motion of the x-axis and the vertical upward throwing motion of the y-axis.
  2. The shadow is moving in a straight line with uniform speed.
  3. The object is directly above the shadow, and the height conforms to the quadratic function, and the height is 0 at the start and end.

So we can draw the shadow first, and then draw the object through the position of the shadow, which is simpler.

private const float MOVE_DURATION = 2f; private float moveTimeLeft = 0f; private Vector3 from; private Vector3 to; private void Update() { ... if(moveTimeLeft<=0) return; moveTimeLeft -= Time.deltaTime; var moveRate = 1 - moveTimeLeft/MOVE_DURATION; var shadowPosition = Vector3.Lerp(from, to, moveRate); _shadow.transform.position = shadowPosition; } public void StartMove(Vector3 fromSet, Vector3 toSet) { from = fromSet; to = toSet; moveTimeLeft = MOVE_DURATION; } 

Here we assume that the moving time is fixed, and the shadow moves to the end at a constant speed according to this time.

Then let's deal with the height of the object, here we introduce a "normalized" quadratic function: $$ y=-\left(2x-1\right)^{2}+1 $$

It looks like this:

enter image description here

When x represents the rate of movement, we can use it to calculate the height of the object.

private const float MOVE_DURATION = 2f; private const float HIGHEST_HEIGHT= 2f; private float moveTimeLeft = 0f; private Vector3 from; private Vector3 to; private void Update() { ... if(moveTimeLeft<=0) return; moveTimeLeft -= Time.deltaTime; var moveRate = 1 - moveTimeLeft/MOVE_DURATION; var shadowPosition = Vector3.Lerp(from, to, moveRate); _shadow.transform.position = shadowPosition; var height = − Mathf.Pow(2*moveRate−1,2) + 1; _flare.transform.position = shadowPosition + Vector3.up * height * HIGHEST_HEIGHT; } public void StartMove(Vector3 fromSet, Vector3 toSet) { from = fromSet; to = toSet; moveTimeLeft = MOVE_DURATION; } 

Because the game is not the real world, you can draw the trajectory according to the effect you want. Here are some examples:

  1. The shadow has a constant speed:
private const float shadowSpeed = 2f; private float moveTimeMax = 0f; private float moveTimeLeft = 0f; public void StartMove(Vector3 fromSet, Vector3 toSet) { from = fromSet; to = toSet; var distance = Vector3.Distance(from,to); moveTimeMax = distance / shadowSpeed; moveTimeLeft = moveTimeMax; } 
  1. The maximum height of the object varies with the launch distance:
private float highestHeight = 0f; private void Update() { ... _flare.transform.position = shadowPosition + Vector3.up * height * highestHeight; ... } public void StartMove(Vector3 fromSet, Vector3 toSet) { ... highestHeight = distance/2; ... } 
  1. Objects are always launched at a 45 degree angle:
public void StartMove(Vector3 fromSet, Vector3 toSet) { ... highestHeight = distance/4; ... } 

In general, if we give up the accurate calculation of physics such as gravity acceleration and velocity, things will be much simpler. We can directly give a motion curve and scale it.

\$\endgroup\$
0
\$\begingroup\$

Simply copy the x,y (but not height z) coords of the projectile.

Copy the terrain height z at this x,y coordinate.

In 3D space, your shadow is at x,y,z.

Project this using orthogonal projection to screen space X,Y.

In your terrain code, make sure you have a way to get the discrete grid X,Y given a 3D vector's x,y, and get the height given this X,Y coord, and translate that height into 3D amount (to scale).

Here's some pseudo code:

float x = _flare.transform.position.X; float y = _flare.transform.position.Y; float z = _terrain.GetHeight((int)x, int(y)); _shadow.transform.position = new Vector3(x, y, z); 
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.