Help figuring out min/max Theta?


(Dan) #1

Hi. I’m working on a missile command clone. My code is mostly working, except I’m having a problem limiting the theta for the incoming angle of my bombs. I’ll explain, and then show the code.

So. the bombs drop from the top of the screen, they always start at y=0, and the ground is at y = 63. The x is random, between 7 and 110. I set a theta for their angle and then move the bombs by incrementing a distance.

What I’m trying to figure out is the minimum and maximum theta based on X so that it will always hit the ground (y = 63) before the line goes off the screen (x < 0 or x > 128).

I thought I had the equation right for figuring out the theta

ArcTan(y2 - y1/x2 - x1);

Here is the code that does it

Since random doesn’t work with floats, I’m multiplying the arctans by 10000 and then / 10000

It mostly works, but sometimes the angles are still too steep

  //Set X orgin and reset distance
  bombs[b].orgx = random(7, 110);
  bombs[b].distance = 0;
  //Create random theta (int) constrained by min and max theta
  int randomTheta = random(atan(63.0 / (4.0 - bombs[b].orgx)) * 10000.0, atan(63.0 / (124.0 - bombs[b].orgx)) * 10000.0);
  //Convert Random Theta back to a float, and then add half pi to 'roate' result to correct axis
  bombs[b].theta = (randomTheta / 10000.0) + (PI / 2);
  //set bomb as active. lower counter
  bombs[b]. active = true;
  warheads--;
  break;

Thanks for the help!


(Pharap) #2

If you add this function:

float map(float value, float inputLow, float inputHigh, float outputLow, float outputHigh)
{
	return (outputLow + (((value - inputLow) * (outputHigh - outputLow)) / (inputHigh - inputLow)));
}

Then you can do:

#include <float.h>

float value = map(static_cast<float>(random()), 0, RAND_MAX, FLT_MIN, FLT_MAX)

Which will get you a random float.
Usually though you want to constrain the value to a specific range, like 0.0f to 1.0f, so you’d write something like:

float value = map(static_cast<float>(random()), 0, RAND_MAX, 0.0f, 1.0f)

As for the maths, I’d have no clue without doing a bit of research and I don’t currently have the time,
but I might be able to have a look if I get time.
(I’ve got a feeling there might be a simple solution using vectors.)

Hopefully someone who’s better at maths will cut in before then though.


(Dan) #3

Brilliant! Thanks Pharap. Let me try implementing this random float function and see what happens, my original guess was something weird was happening with the back and forth casting. I’ll break down the math (or at least what I think it is) if it’s still not working. Thank you so much again!


(Pharap) #4

Could be. It could also be due to truncation though.

random returns a long, which is 32-bits wide on the Arduboy.
int on the other hand is 16-bits wide on the Arduboy, giving it an effective range of -32768 to 32767.

No problem.


Earlier I thought about the angle thing in terms of points and vectors,
and a solution ocurred to me.

Take the point at the top of the screen (created from y = 0 and 7 <= x <= 110) and call that origin.
Then take points { 0, 63 } and { 127, 63 } and call those point0 and point1.
Together these three points form a triangle.

For a missile to travel from point origin to the bottom of the screen without going outside the horizontal boundaries of the screen, the missile’s velocity must keep it within that triangle.

So essentially, the velocity vector must be generated so that it is between the vector from origin to point0 and the vector from origin to point1.

float linearInterpolate(float low, float high, float factor)
{
	return (((1 - factor) * low) + (factor * high));
}

Vector2 generateNormalisedVelocity(Point2 origin, Point2 point0, Point2 point1)
{
	long r = random();
	float factor = map(static_cast<float>(r), 0, RAND_MAX, 0.0f, 1.0f)
	Vector2 first = normalise(point0 - origin);
	Vector2 second = normalise(point1 - origin);
	float x = linearInterpolate(first.x, second.x, factor);
	float y = linearInterpolate(first.y, second.y, factor);
	return Vector2(x, y);
}

So you take the vectors forming the sides of the triangle, normalise them and then linearly interpolate (‘lerp’) between them using a random floating point value between 0 and 1.

Does that make sense at all?

It would be computationally cheaper to lerp and then normalise,
but I’m not sure how much that would impact the results.
(I’m a better programmer than I am a mathematician.)

As for doing it with angles, presumably you could take the same approach by figuring out the angle/slope of the sides of the triangle, but I think that might be more computationally expensive, and certainly to generate a velocity vector from an angle you’d need to use sin and cos, which already makes it probably more expensive than the other approach.
(normalise is the only expensive operation in my proposed solution, because it requires use of sqrt.)

If you’re unsure of how Vector2 and Point2 work here,
have a look at the code from my Physix demo:


You’d just have to substitute Number and NumberU with float.
(I substituted float for a fixed point type because part of the reason I wrote Physix was to test out some fixed point types that I wrote.)

I haven’t implemented normalise in that code, but that’s fairly straightforward.
It’s just:

Vector2 normalise(Vector2 vector)
{
	float magnitudeSquared = vector.getMagnitudeSquared();
	
	// Already normalised
	if(magnitudeSquared == 1)
		return vector;
		
	return (vector / sqrt(magnitudeSquared));
}

(The test for whether the vector was already normalised isn’t strictly necessary, but it’s useful for saving processing power if you have to use normalise a lot.)


(Bert Veer) #5

Hi people, my first post here. Recently got my Arduboy and loving it. Will publish my game shortly :slight_smile:

To the question, it seems you are overthinking the problem. Couldn’t you just pick a random point on the target line and calculate the vector/angle with that? Min and max values could also be easily determined this way.


(Pharap) #6

Good point.

Vector2 generateRandomVelocity()
{
	const long startX = random(7, 110);
	const long endX = random(7, 110);

	const Point startPoint = Point(startX, 0);
	const Point endPoint = Point(endX, 63);

	return normalise(endPoint - startPoint);
}

I’m annoyed that I didn’t think of that.