Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
MJoergen committed Oct 26, 2020
1 parent 556b8aa commit 76b2494
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 9 deletions.
2 changes: 2 additions & 0 deletions c/test_programs/tennis.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const int PLAYER_SPEED = 2; // pixels/frame
const int PLAYER_RADIUS = 16; // pixels
const int SCREEN_LEFT = 8; // pixels
const int SCREEN_RIGHT = 632; // pixels
const int SCREEN_BOTTOM = 480; // pixels
const int SCREEN_TOP = 12; // pixels
const int WHITE_SQUARE = PAL_FG_YELLOW + PAL_BG_YELLOW + 0x20; // palette and character
const int BALL_RADIUS = 8; // pixels
const int VEL_SCALE = 512;
Expand Down
2 changes: 2 additions & 0 deletions c/test_programs/tennis.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ extern const int PLAYER_SPEED;
extern const int PLAYER_RADIUS;
extern const int SCREEN_LEFT;
extern const int SCREEN_RIGHT;
extern const int SCREEN_BOTTOM;
extern const int SCREEN_TOP;
extern const int WHITE_SQUARE;
extern const int BALL_RADIUS;
extern const int VEL_SCALE;
Expand Down
193 changes: 192 additions & 1 deletion c/test_programs/tennis_ball.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,117 @@
static t_vec position;
static t_vec velocity;

extern t_vec player_position; // Position of player

/*
* This is an optimized multiply routine.
* It takes two 16-bit signed inputs and returns a 32-bit signed output.
*/
static long muls(int arg1, int arg2)
{
// Using a union is much faster than performing explicit shifts.
union {
long l;
int i[2];
} u;

MMIO(IO_EAE_OPERAND_0) = arg1;
MMIO(IO_EAE_OPERAND_1) = arg2;
MMIO(IO_EAE_CSR) = EAE_MULS;
u.i[0] = MMIO(IO_EAE_RESULT_LO); // This implicitly assumes little-endian.
u.i[1] = MMIO(IO_EAE_RESULT_HI);
return u.l;
} // end of muls


/*
* This function calculates (x*y)/z.
*/
static int muldiv(long x, long y, long z)
{
// Since the multiplication x*y may overflow, we instead scale x and z with
// the constant value k.
const int k = VEL_SCALE;

// We write x = q*k + r, where r = x mod k.
const long q = x/k;
const long r = x - q*k; // |r| < k

// We write z = s*k + t, where t = z mod k.
const long s = z/k;
const long t = z - s*k; // |t| < k

// We write q*y = a*s + p, where p = q*y mod s.
const long a = (q*y)/s;
const long p = q*y - a*s; // |p| < s

// At this stage, "a" is a first approximation to (x*y)/z.

// Now we calculate the deviation u=x*y-a*z
// = (q*k+r)*y - a*(s*k+t)
// = (q*y-a*s)*k + r*y - a*t
// This calculation won't overflow, because |p*k| < |s*k| < |z|.
const long u = p*k + r*y - a*t;

// A better approximation is now "a + u/z".

// Finally handle the rounding, based on the deviation.
int res = a;
if (u > z/2)
res += 1;
if (u < -z/2)
res -= 1;

return res;
} // end of muldiv

/*
* This function is written based on this article:
* https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional_collision_with_two_moving_objects
*/
static t_vec calcNewVelocity(t_vec pos1, t_vec pos2, t_vec vel1, t_vec vel2)
{
t_vec delta_pos = {.x=pos1.x-pos2.x, .y=pos1.y-pos2.y};
t_vec delta_vel = {.x=vel1.x-vel2.x, .y=vel1.y-vel2.y};

// Since the radius is at most 16, the total distance is at most 32. With a scaling factor of 32,
// the largest value of delta_pos.xy is 32*32 = 2^10. So the largest value of dpdp is 2^20.
long dpdv = muls(delta_pos.x,delta_vel.x) + muls(delta_pos.y,delta_vel.y);
long dpdp = muls(delta_pos.x,delta_pos.x) + muls(delta_pos.y,delta_pos.y);

t_vec result = vel1;
if (dpdv < 0)
{
long dividend = 2L*(-dpdv);
long divisor = dpdp;

result.x += muldiv(dividend, delta_pos.x, divisor);
result.y += muldiv(dividend, delta_pos.y, divisor);
}

return result;
} // end of calcNewVelocity


static void collision_point(t_vec otherPos, int distance)
{
t_vec delta_pos = {.x=otherPos.x-position.x, .y=otherPos.y-position.y};

long x2 = muls(delta_pos.x, delta_pos.x);
long y2 = muls(delta_pos.y, delta_pos.y);
long r2 = muls(distance, distance);
const t_vec zero = {0, 0};

if (x2+y2 < r2)
{
velocity = calcNewVelocity(
position,
otherPos,
velocity,
zero);
}
} // end of collision_point


/*
* This function is called once at start of the program
Expand Down Expand Up @@ -46,11 +157,91 @@ void ball_draw()
/*
* This function is called once per frame, i.e. 60 times a second
*/
void ball_update()
int ball_update()
{
position.x += velocity.x / (VEL_SCALE/POS_SCALE);
position.y += velocity.y / (VEL_SCALE/POS_SCALE);

/* Collision against left wall */
if (position.x < POS_SCALE*(SCREEN_LEFT+BALL_RADIUS))
{
position.x = POS_SCALE*(SCREEN_LEFT+BALL_RADIUS);

if (velocity.x < 0)
{
velocity.x = -velocity.x;
}
}

/* Collision against right wall */
if (position.x > POS_SCALE*(SCREEN_RIGHT-BALL_RADIUS))
{
position.x = POS_SCALE*(SCREEN_RIGHT-BALL_RADIUS);

if (velocity.x > 0)
{
velocity.x = -velocity.x;
}
}

/* Collision against top wall */
if (position.y < POS_SCALE*SCREEN_TOP)
{
position.y = POS_SCALE*SCREEN_TOP;

if (velocity.y < 0)
{
velocity.y = -velocity.y;
}
}

/* Collision against left side of barrier */
if (position.x > POS_SCALE*(BAR_LEFT-BALL_RADIUS) && position.y > POS_SCALE*(BAR_TOP-BALL_RADIUS))
{
position.x = POS_SCALE*(BAR_LEFT-BALL_RADIUS);

if (velocity.x > 0)
{
velocity.x = -velocity.x;
}
}

/* Collision against right side of barrier */
if (position.x < POS_SCALE*(BAR_RIGHT+BALL_RADIUS) && position.y > POS_SCALE*(BAR_TOP-BALL_RADIUS))
{
position.x = POS_SCALE*(BAR_RIGHT+BALL_RADIUS);

if (velocity.x < 0)
{
velocity.x = -velocity.x;
}
}

/* Collision against top side of barrier */
if (position.y > POS_SCALE*(BAR_TOP-BALL_RADIUS) &&
position.x > POS_SCALE*(BAR_LEFT-BALL_RADIUS) &&
position.x < POS_SCALE*(BAR_RIGHT+BALL_RADIUS))
{
position.y = POS_SCALE*(BAR_TOP-BALL_RADIUS);

if (velocity.y > 0)
{
velocity.y = -velocity.y;
}
}

const t_vec barTopLeft = {BAR_LEFT, BAR_TOP};
const t_vec barTopRight = {BAR_RIGHT, BAR_TOP};

/* Collision against barrier corners */
collision_point(barTopLeft, BALL_RADIUS);
collision_point(barTopRight, BALL_RADIUS);

/* Collision against player */
collision_point(player_position, PLAYER_RADIUS+BALL_RADIUS);

velocity.y += GRAVITY;

return 0;
} // end of ball_update

16 changes: 8 additions & 8 deletions c/test_programs/tennis_player.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
static int move_left = 0; // Current status of LEFT cursor key
static int move_right = 0; // Current status of RIGHT cursor key

static t_vec position = {200, 480}; // Initial position of player
t_vec player_position = {200, 480}; // Initial position of player


/*
Expand Down Expand Up @@ -33,7 +33,7 @@ void player_draw()
* left corner of the sprite. Therefore, we have to adjust the coordinates
* with the size of the sprite.
*/
sprite_set_position(0, position.x-PLAYER_RADIUS, position.y-PLAYER_RADIUS);
sprite_set_position(0, player_position.x-PLAYER_RADIUS, player_position.y-PLAYER_RADIUS);
} // end of player_draw


Expand Down Expand Up @@ -62,16 +62,16 @@ int player_update()
/* Move player */
if (move_right && !move_left)
{
position.x += PLAYER_SPEED;
if (position.x >= BAR_LEFT - PLAYER_RADIUS)
position.x = BAR_LEFT - PLAYER_RADIUS;
player_position.x += PLAYER_SPEED;
if (player_position.x >= BAR_LEFT - PLAYER_RADIUS)
player_position.x = BAR_LEFT - PLAYER_RADIUS;
}

if (!move_right && move_left)
{
position.x -= PLAYER_SPEED;
if (position.x < SCREEN_LEFT + PLAYER_RADIUS)
position.x = SCREEN_LEFT + PLAYER_RADIUS;
player_position.x -= PLAYER_SPEED;
if (player_position.x < SCREEN_LEFT + PLAYER_RADIUS)
player_position.x = SCREEN_LEFT + PLAYER_RADIUS;
}

return 0;
Expand Down

0 comments on commit 76b2494

Please sign in to comment.