Sin and cos not working.

Post Reply
GreenDude2
Posts: 1
Joined: Sat Jan 22, 2022 12:44 am

Sin and cos not working.

Post by GreenDude2 » Sat Jan 22, 2022 1:04 am

Hi! Why is sin and cos not working?
I have math.h imported. but sin and cos are not working. Why is this?
Code:

Code: Select all

#include "Intellisense.h"
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include "gba_input.h"
#include <stdint.h>
#include <math.h>
#include <stdbool.h>

typedef uint8_t		u8;		typedef int8_t		s8;
typedef uint16_t	u16;	typedef int16_t		s16;
typedef uint32_t	u32;	typedef int32_t		s32;

typedef volatile uint8_t		v_u8;	typedef volatile int8_t			v_s8;
typedef volatile uint16_t		v_u16;	typedef volatile int16_t		v_s16;
typedef volatile uint32_t		v_u32;	typedef volatile int32_t		v_s32;

#define REG_DISPCNT *((v_u32*)(0x04000000))
#define VIDEOMODE_3 0x0003
#define BGMODE_2	0x0400

#define SCREENBUFFER ((v_u16*)(0x06000000))
#define SCREEN_W 240
#define SCREEN_H 160

#define PI 3.14159265

double sin(double o){
	if (o > 6)
	{
		double y = o - 6;
	} else {
		double y = o;
	}
	
	if (o == 0.1)
	{
		return 0.09983341664;
	}
	if (o == 0.2)
	{
		return 0.19866933079;
	}
	if (o == 0.3)
	{
		return 0.29552020666;
	}
	if (o == 0.4)
	{
		return 0.3894183423;
	}
	if (o == 0.5)
	{
		return 0.4794255386;
	}
	if (o == 0.6)
	{
		return 0.56464247339;
	}
	if (o == 0.7)
	{
		return 0.64421768723
	}
	if (o == 0.8)
	{
		return 0.7173560909;
	}
	if (o == 0.9)
	{
		return 0.78332690962;
	}
	if (o == 0)
	{
		return 0;
	}
}

void delay(int ms)
{
	for (int i = 0; i < ms; i++)
	{
		VBlankIntrWait();
	}
	
}
s32 abs(s32 a_val)
{
	s32 mask = a_val >> 31;
	return (a_val ^ mask) - mask;
}

u16 setColor(u8 a_red, u8 a_green, u8 a_blue)
{
	return (a_red & 0x1F) | (a_green & 0x1F) << 5 | (a_blue & 0x1f) << 10;
}

void plotPixel( u32 a_x, u32 a_y, u16 a_colour)
{
	SCREENBUFFER[a_y * SCREEN_W + a_x] = a_colour;
}

void drawRect(u32 a_left, u32 a_top, u32 a_width, u32 a_height, u16 a_color)
{
	for (u32 y = 0; y < a_height; ++y)
	{
		for (u32 x = 0; x < a_width; ++x)
		{
			SCREENBUFFER[ (a_top + y) * SCREEN_W + a_left + x ] = a_color;
		}
	}
}

void drawLine(u32 a_x, u32 a_y, u32 a_x2, u32 a_y2, u16 a_colour)
{
	//Get the horizontal and vertical displacement of the line
	s32 w = a_x2 - a_x; //w is width or horizontal distance
	s32 h = a_y2 - a_y; //h is the height or vertical displacement
	//work out what the change in x and y is with the d in these variables stands for delta
	s32 dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0;

	if (w<0) dx1 = dx2 = -1; else if (w>0) dx1 = dx2 = 1;
	if (h<0) dy1 = -1; else if (h>0) dy1 = 1;
	//which is the longest the horizontal or vertical step
	s32 longest = abs(w); //assume that width is the longest displacement
	s32 shortest = abs(h);
	if ( shortest > longest )	//oops it's the other way around reverse it
	{
		//use xor to swap longest and shortest around
		longest ^= shortest; shortest ^= longest; longest ^= shortest;
		if (h<0) dy2 = -1; else if (h>0) dy2 = 1;
		dx2 = 0;
	}
	//geta  value that is half the longest displacement
	s32 numerator = longest >> 1;
	//for each pixel across the longest span
	for (s32 i = 0; i <= longest; ++i)
	{
		//fill the pixel we're currently at
		plotPixel( a_x, a_y, a_colour);
		//increase the numerator by the shortest span
		numerator += shortest;
		if (numerator>longest)  
		{
			//if the numerator is now larger than the longest span
			//subtract the longest value from the numerator
			numerator -= longest;
			//increment x & y by their delta1 values
			a_x += dx1;
			a_y += dy1;
		}
		else 
		{
			//numerator is smaller than the longst side
			//increment x & y by their delta 2 values
			a_x += dx2;
			a_y += dy2;
		}
	}
}

int x = 5, y = 5, pa = PI/2, pdx, pdy;

int main(void) {
	REG_DISPCNT = VIDEOMODE_3 | BGMODE_2;
	irqInit();
	irqEnable(IRQ_VBLANK);

	while (1) {
		PollKeys();
		if (keyDown(LEFT))
		{
			pa-=0.1;
			if (pa < 2*PI){ pa -= 2*PI; }
			pdx = cos(pa) * 5;
			pdy = sin(pa) * 5;
		}
		if (keyDown(RIGHT))
		{
			pa+=0.1; if (pa > 2*PI){ pa += 2*PI; }
			pdx = cos(pa) * 5;
			pdy = sin(pa) * 5;
		}
		
		if (keyDown(UP))
		{
			x += pdx;
			y += pdy;
		}
		if (keyDown(DOWN))
		{
			x -= pdx;
			y -= pdy;
		}
		plotPixel(x, y, setColor(31, 31, 0));
		delay(3);
		plotPixel(x, y, setColor(0, 0, 0));
	}
}
Why is this not working?
P.S. I am following a tutorial.

WinterMute
Site Admin
Posts: 1986
Joined: Tue Aug 09, 2005 3:21 am
Location: UK
Contact:

Re: Sin and cos not working.

Post by WinterMute » Sat Feb 19, 2022 1:06 am

Apologies for taking so long to reply. I've had a few other things going on and it was a little difficult to try and decipher the intent of your code. I spent a little time getting it to build with stock libgba headers too - it helps bring a bit of consistency & understanding. Your code seems a little confused and I assume it's fair to say that you're never really written C before.

The first issue was, I guess, that you didn't know that using floating point math also requires linking against libm where sin & cos live. This is done in our stock templates by adding -lm to the LIBS line.

You were also using integer variables to hold floating point values which doesn't really help. In C, unlike some other languages, the type of data is declared ahead of time rather than being inferred from the kind of data you're attempting to store in them. Integers (or ints) can only store whole numbers with no fractional part. In math.h sin & cos are defined as functions which take a double as an argument & return a double as a result. Doubles are 64bit numbers which represent floating point values.

math.h defines M_PI with a reasonable value for pi rather than attempting to define your own.

The formula you were using to wrap the angle defining direction was a little off.

Finally, the way you were plotting a pixel here would result in nothing being visible on screen at all. VBlankIntrWait waits for the period when data from the framebuffer isn't being transferred to the LCD. If you plot a pixel in your foreground color, wait for vblank, and then immediately erase it by plotting the background color over it then you'll never see anything on screen because it hasn't been displayed yet.

Once I got your code doing what I thought it was supposed to I may have gotten a little carried away and implemented some very basic physics for illustration. Beginners quite often attempt to slow down movement by inserting delays rather than the fractional movement favoured by experienced devs. Physics sounds like something difficult but, as I hope I've showed here, it really doesn't have to be. Something as simple as building up to full speed and slowly coming to a halt can make a very big difference to how movement feels in a game.

This is the final code which I hope is reasonably understandable but please do feel free to ask if it's not.

Code: Select all

#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <gba_video.h>

#include <math.h>

// The mode 2 framebuffer is at the start of VRAM
u16 * const framebuffer = (u16*)VRAM;

void plotPixel( u32 x, u32 y, u16 colour)
{
	framebuffer[y * SCREEN_WIDTH + x] = colour;
}

// starting position in centre of screen
#define START_X SCREEN_WIDTH/2
#define START_Y SCREEN_HEIGHT/2

double x = START_X, y = START_Y; 
// previous position so we only update when position changes
double lastX = START_X, lastY = START_Y;

// Initial direction is 90° or straight up the screen 
double pa = M_PI/2;
double pdx, pdy;

// start at rest
double velocity = 0;

// variables controlling our simple physics
// maximum speed we want to travel at - this is pixels per frame
static const double max_velocity = 2.5;
// how much we reduce speed each frame. This simulates friction of sorts
static const double deceleration = 0.01;
// how much speed our thruster adds each frame. Needs to be > deceleration or we won't move
static const double acceleration = 0.02;

// how far we want to turn each frame
// We have roughly 60 frames per second and 2π radians in a full circle
// dividing 2π by 120 means we take 120 frames or around 2 seconds for a full turn
static const double turnspeed = 2 * M_PI / 120;

int main(void) {
	// Set the video mode and enable background 2
	REG_DISPCNT = MODE_3 | BG2_ON;
	// Initialise libgba interrrupt dispatcher 
	irqInit();
	// Enable the vblank irq so we can use the system VBlankIntrWait.
	// libgba dispatcher will handle acknowledgement and setting flags for bios intrwait functions
	irqEnable(IRQ_VBLANK);

	// mode 2 is 16bit per pixel, 5 bits per component.
	// libgba provides RGB5 for readable colors 
	plotPixel(x, y, RGB5(31, 31, 0));

	while (1) {  // loop forever
		// libgba provides some basic button handling
		// scanKeys should be called once per loop
		scanKeys();
		// keysHeld tells us which buttons are currently pressed.
		// We also have keysDown and keysUp which tell us which buttons changed state in this loop
		int held = keysHeld();
		if (held & KEY_LEFT)
		{
			pa-=(turnspeed);
			if (pa < 0){ pa += 2*M_PI; } // wrap direction at one complete circle.
		}
		if (held & KEY_RIGHT)
		{
			pa+=(turnspeed);
			if (pa > 2*M_PI){ pa -= 2*M_PI; }
		}
		
		// A button increases velocity
		if (held & KEY_A) velocity += acceleration;
		// B button decreases it
		if (held & KEY_B) velocity -= acceleration;

		// we cap the velocity at something sensible 
		if (velocity > max_velocity) velocity = max_velocity;
		if (velocity < -max_velocity) velocity = -max_velocity;

		// Use trig to calculate x and y components of movement
		pdx = cos(pa) * velocity;
		pdy = sin(pa) * velocity;

		// Update our position
		x += pdx;
		y += pdy;

		// wrap our postion around the screen so we don't wander out of framebuffer
		if (x>SCREEN_WIDTH) x -= SCREEN_WIDTH;
		if (y>SCREEN_HEIGHT) y -= SCREEN_HEIGHT;
		if (x<0) x += SCREEN_WIDTH;
		if (y<0) y += SCREEN_HEIGHT;

		// If we're moving then apply our pseudo friction
		if (velocity > 0) velocity -= deceleration;
		if (velocity < 0) velocity += deceleration;

		// Now wait for the vblank period when framebuffer isn't being transferred to LCD
		VBlankIntrWait();

		// It's good practice to only update if something changed
		if (lastX != x || lastY != y) {
			// erase last position - make it black
			plotPixel(lastX, lastY, RGB5(0, 0, 0));
			// show current position - make it white 
			plotPixel(x, y, RGB5(31, 31, 31));
			lastX = x; lastY = y;
		}
	}
}
I've attached the complete project I ended up with so you. can have a play with that
sincostest.zip
(3.66 KiB) Downloaded 312 times
It's probably also worth mentioning that the GBA doesn't have floating point hardware so this isn't really considered good practice. It's fine for an explainer and introducing the concepts and may even suffice for a simple game but will ultimately limit what you can do. I'll try and dig out some spare cycles to explain fixed point and perhaps add some LUT based trig functions to libgba.
Help keep devkitPro toolchains free, Donate today

Personal Blog

Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests