I'd start by packing all the variables for a single projectile into a struct. You don't absolutely need all these values, but they might come in handy later:
- Code: Select all
struct projectile
{
int start_x, start_y;
int target_x, target_y;
float current_x, current_y;
float velocity_x, velocity_y;
int num_frames;
};
void init_projectile(projectile & proj, int start_x, int start_y, int target_x, int target_y, int num_frames)
{
proj.start_x = proj.current_x = start_x;
proj.start_y = proj.current_y = start_y;
proj.target_x = target_x;
proj.target_y = target_y;
proj.velocity_x = (float)(target_x - start_x) / num_frames;
proj.velocity_y = (float)(target_y - start_y) / num_frames;
proj.num_frames = num_frames;
}
void update_projectile(projectile & proj)
{
proj.current_x += proj.velocity_x;
proj.current_y += proj.velocity_y;
}
Call update_projectile once per frame and it will update the current position, so all you have to do is draw a line between <start-x, start_y> and <current_x, current_y>.
That will be enough to get started, but there are two problems with this approach. The first is that you'll get very small round-off errors building up over time. This isn't a problem for most games, but if you want to do it 100% perfect then you can get rid of those errors by passing in the frame_number (which should be relative to the first frame that the projectile was fired on):
- Code: Select all
void update_projectile(projectile & proj, int frame_num)
{
proj.current_x = proj.start_x + (float)frame_num * (proj.target_x - proj.start_x) / proj.num_frames;
proj.current_y = proj.start_y + (float)frame_num * (proj.target_y - proj.start_y) / proj.num_frames;
}
The second problem is that your lines will "wobble" across multiple frames. This is the result of aliasing and is due to the fact that your floating-point variable end-points are constantly changing and being "snapped" to the nearest integer. A very simple solution to this is to replace the Gamebuino drawLine function with one that also accepts a cutoff value for y:
- Code: Select all
void drawProjectile(int8_t x0, int8_t y0, int8_t x1, int8_t y1, float cutoff) {
And then further on in the function replace this:
- Code: Select all
if (steep) {
drawPixel(y0, x0);
} else {
drawPixel(x0, y0);
}
With this:
- Code: Select all
if (steep) {
if (x0 > cutoff) drawPixel(y0, x0);
} else {
if (y0 > cutoff) drawPixel(x0, y0);
}
And then to use this function you pass in the projectile's start and target points along with the current y:
drawProjectile(proj.start_x, proj.start_y, proj.target_x, proj.target_y, proj.current_y).
What this will effectively do is cause the function to always iterate over every pixel in the line using the same start/target endpoints, but any pixels above the current_y won't actually be drawn on screen. This will give the illusion that the end-point is moving whilst also eliminating the jitter.