ProgramGamer
|
|
« on: August 07, 2015, 04:13:37 AM » |
|
So, I've been messing with C++ again lately, and I thought I'd post about my adventures in Stroustrup land. So, for the first post, I've been toying with SDL2 and it's functions lately. I downloaded Lazy Foo's tutorial file and started extrapolating that into something. So far there are color cycling, easing functions and inverted color cube movement. Here's the code: (also epilepsy warning I guess even though the color cycling is not that fast and the screen size is not that big) //Using SDL and standard IO #include <SDL.h> #include "stdio.h"
//Screen dimension constants const int SCREEN_WIDTH = 256; const int SCREEN_HEIGHT = 192;
int EaseInOutQuad(double t,double b,double c,double d) { //t: current time //b: start value //c: change in value //d: duration t /= d/2; if (t < 1) return c/2*t*t + b; t--; return -c/2 * (t*(t-2) - 1) + b; }
int main( int argc, char* args[] ) { //The window we'll be rendering to SDL_Window* window = NULL;
//The surface contained by the window SDL_Surface* screenSurface = NULL;
//Screen buffer SDL_Surface* screenBuffer = NULL;
//Initialize SDL if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() ); } else { //Create window window = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN ); if( window == NULL ) { printf( "Window could not be created! SDL_Error: %s\n", SDL_GetError() ); } else { //Get window surface screenSurface = SDL_GetWindowSurface( window );
//Create a surface screenBuffer = SDL_CreateRGBSurface(0,SCREEN_WIDTH,SCREEN_HEIGHT,32,0,0,0,0); if (screenBuffer == NULL) { printf( "Could not create buffer! SDL_Error: %s\n", SDL_GetError() ); }
//Game loop bool running = true; SDL_Event event; int colorRed = 255; int colorGreen = 255; int colorBlue = 255; int const increment = 1; int x = 0; int y = 0; int xSpeed = 0; int ySpeed = 0; while (running) { //Event handling while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { running = false; } else if (event.type == SDL_KEYDOWN) { if (event.key.keysym.scancode == SDL_SCANCODE_LEFT) { xSpeed = -4; } else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT) { xSpeed = 4; } else if (event.key.keysym.scancode == SDL_SCANCODE_UP) { ySpeed = -4; } else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN) { ySpeed = 4; } } else if (event.type == SDL_KEYUP) { if (event.key.keysym.scancode == SDL_SCANCODE_LEFT) { xSpeed = 0; } else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT) { xSpeed = 0; } else if (event.key.keysym.scancode == SDL_SCANCODE_UP) { ySpeed = 0; } else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN) { ySpeed = 0; } } }
//Moves the player around x += xSpeed; y += ySpeed;
//Cycle through colors if (colorRed < 255 && colorGreen == 0 && colorBlue == 0) { colorRed += increment; } else if (colorRed == 255 && colorGreen < 255 && colorBlue == 0) { colorGreen += increment; } else if (colorRed > 0 && colorGreen == 255 && colorBlue == 0) { colorRed -= increment; } else if (colorRed == 0 && colorGreen == 255 && colorBlue < 255) { colorBlue += increment; } else if (colorRed == 0 && colorGreen > 0 && colorBlue == 255) { colorGreen -= increment; } else if (colorRed < 255 && colorGreen == 0 && colorBlue == 255) { colorRed += increment; } else if (colorRed == 255 && colorGreen == 0 && colorBlue > 0) { colorBlue -= increment; } else if (colorRed > 0 && colorGreen == 255 && colorBlue == 255) { colorRed -= increment; }
//Draw a colored rectangle to the screen SDL_FillRect(screenBuffer, NULL, SDL_MapRGB(screenSurface->format,EaseInOutQuad(colorRed,0,255,255),EaseInOutQuad(colorGreen,0,255,255),EaseInOutQuad(colorBlue,0,255,255)));
//Draw a square on top of this SDL_Rect rect; rect.x = x; rect.y = y; rect.w = 32; rect.h = 32; SDL_FillRect(screenBuffer, &rect, SDL_MapRGB(screenSurface->format,255 - EaseInOutQuad(colorRed,0,255,255),255 - EaseInOutQuad(colorGreen,0,255,255),255 - EaseInOutQuad(colorBlue,0,255,255)));
//Blit the buffer to the screen surface if (SDL_BlitSurface(screenBuffer,NULL,screenSurface,NULL)!=0) { printf( "Window could not be blit! SDL_Error: %s\n", SDL_GetError() ); }
//Update the surface SDL_UpdateWindowSurface( window );
//Wait one frame SDL_Delay(17); } //Free the surface after game loop SDL_FreeSurface(screenBuffer); } } //Destroy window SDL_DestroyWindow( window );
//Quit SDL subsystems SDL_Quit();
return 0; }
So yeah, I had to deal with SDL's input system, which is... great. Having to cycle through a stack of inputs is just fine and not at all tedious. I had less trouble drawing to the screen surprisingly, and was even able to make an intermediate level color cycling routine. I even ported an easing function to make it smoother, which worked wonderfully! The only issue I can find is that clicking and dragging the window pauses the program for some reason, so I'll look into that next.
|
|
|
Logged
|
|
|
|
JWki
|
|
« Reply #1 on: August 07, 2015, 05:07:13 AM » |
|
SDL's message loop is simply a wrapper over the message loop of whatever OS you are using, so it's not really designed to provide a nice input system, it's just not that high level and it shouldn't. Despite the many people using SDL as "engine", it's mainly a platform abstraction layer with some additional rendering capabilities. It's something to build an engine on top of.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #2 on: August 07, 2015, 05:27:42 AM » |
|
I agree, it's really not an engine by itself, but can be made into an engine with relative ease thanks to its functions and structures. Speaking of which, I should probably make some kind of controller object to alleviate the main function of all that input looping. I would just have to call a "CheckInput" method to empty the input queue and could just call another "GetInput" method to return a bool whether a key has been pressed or not. That would be practical.
|
|
|
Logged
|
|
|
|
Cheezmeister
|
|
« Reply #3 on: August 07, 2015, 07:56:01 PM » |
|
SDL's input API is pretty dope for what it is. So's the rest of it, for that matter. Truly superb design from some real OG gamedevs. The fact that it's NOT an engine makes it reliable, flexible, portable, and generally a minimal pain in the arse compared to anything else I've tried. Is it tedious to write your own input loop? Well, sure, a little, but it's not rocket science either. Here we have a solid input translator in under 100 lines, that offers NES controls and then some. It ain't engine-caliber, but it covers all of my bases, and probably most of yours. (From https://github.com/Cheezmeister/jamboot) Input handle_input() { Input ret = {0};
// Process events first SDL_Event event; while (SDL_PollEvent(&event) ) { if (event.type == SDL_QUIT) ret.quit = true; if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) ret.quit = true;
// Window resize if (event.type == SDL_WINDOWEVENT) { if (event.window.event == SDL_WINDOWEVENT_RESIZED) { int x = event.window.data1; int y = event.window.data2; viewport.x = x; viewport.y = x; int max = x > y ? x : y; glViewport(0, 0, max, max); } }
// Gamepad buttons if (event.type == SDL_CONTROLLERBUTTONDOWN) { if (event.cbutton.button & SDL_CONTROLLER_BUTTON_A) { ret.action.prime = true; } } // Keyboard presses if (event.type == SDL_KEYDOWN) { if (event.key.keysym.sym == SDLK_SPACE) { ret.action.prime = true; } } }
// Poll the current state of the mouse struct _Mouse { Uint8 buttons; int x; int y; } mouse; mouse.buttons = SDL_GetMouseState(&mouse.x, &mouse.y); ret.held.prime |= (mouse.buttons & SDL_BUTTON(1)); ret.axes.x2 = mouse.x * 2.0 / viewport.x - 1.0; ret.axes.y2 = mouse.y * 2.0 / viewport.y - 1.0; ret.axes.y2 *= -1;
// Poll the current state of the keyboard const Uint8* keystate = SDL_GetKeyboardState(NULL); ret.axes.y1 = 1.0 * (keystate[SDL_SCANCODE_UP]); ret.axes.y1 -= 1.0 * (keystate[SDL_SCANCODE_DOWN]); ret.axes.x1 = 1.0 * (keystate[SDL_SCANCODE_RIGHT]); ret.axes.x1 -= 1.0 * (keystate[SDL_SCANCODE_LEFT]);
// Poll the gamepad sticks ret.axes.x3 = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX) / (float)32767; ret.axes.x4 = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) / (float)32767; ret.axes.y3 = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY) / (float)32767; ret.axes.y4 = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) / (float)32767;
// Poll gamepad buttons ret.held.aux = SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
return ret; }
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #4 on: August 08, 2015, 03:44:01 AM » |
|
Oh, wow, the architecture on that is quite amazing! You have a class called Input which you use as your return type and which you fill with input info every time you call the function handle_input(). That is a new level of smarts for me, I salute you good sir(s?)! Also, thank you for providing that, I was working on something else and I was going completely the wrong way. I was creating an entire class for controllers with virtual keys and stuff like that, your method is way simpler and still as efficient. The only thing I can see that could improve it would be to make handle_input() a member of a class which keeps track of virtual keys so that you could have remappable buttons. Thanks to you sir and have a good day!
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #5 on: August 08, 2015, 04:46:59 PM » |
|
To add to this, I messed around with SDL's ability to detect touch input with my new surface pro, and it was surprisingly easy to program with SDL's events, so I stand corrected on the matter I guess
|
|
|
Logged
|
|
|
|
Cheezmeister
|
|
« Reply #6 on: August 08, 2015, 10:10:52 PM » |
|
Thanks for the kinds words. Though, as I said...it's really not rocket science. This is rocket science SDL makes everything super easy. You have Sam Lantinga & co. to thank for that, I just make crappy games with it =] I would, however, be very interested in seeing your touch handling code.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #7 on: August 09, 2015, 04:33:40 PM » |
|
Wow... I was trying to make an enemy object and I just tried to put the definition of the class in a .cpp file and the implementation in a .h. Someone get c++ away from me as quickly as possible...
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #8 on: August 12, 2015, 04:42:09 PM » |
|
Alrighty, I can't figure this one out. I am clearly calling five different SDL_FillRect functions here, why are they not showing up on the screen??? #include <stdio.h> #include <math.h> #include "SDL.h"
using namespace std;
const int windowWidth = 800; const int windowHeight = 450;
int main(int argc, char *argv[]) { //Initiation function if (SDL_Init(SDL_INIT_EVERYTHING)) { printf("SDL Not initiated for some reason."); }
//Creating a window SDL_Window* window = SDL_CreateWindow("Game",300,50,windowWidth,windowHeight,0); if (window == NULL) { printf("Window not created for some reason"); }
//Getting the window's surface SDL_Surface* surface = SDL_GetWindowSurface(window); if (surface == NULL) { printf("Surface not retrieved for some reason"); }
SDL_Surface* buffer = SDL_CreateRGBSurface(0,windowWidth,windowHeight,32,0,0,0,255);
//Setting up game loop variables and memory in general bool running = true; double x = 0; double y = 0; int square_x = 0; int square_y = 0; int red = 0; int green = 0; int blue = 0; SDL_Event event; //Rectangle areas SDL_Rect bar_1; bar_1.w = windowWidth / 16; bar_1.h = windowHeight;
SDL_Rect area_1; area_1.w = windowWidth / 16 * 4; area_1.h = windowHeight; area_1.x = bar_1.x + bar_1.w;
SDL_Rect bar_2_1; SDL_Rect bar_2_2; bar_2_1.w = windowWidth / 16; bar_2_1.h = windowHeight; bar_2_1.x = area_1.x + area_1.w;
SDL_Rect area_2; area_2.w = windowWidth / 16 * 9; area_2.h = windowHeight; area_2.x = bar_2_1.x + bar_2_1.w;
SDL_Rect bar_3; bar_3.w = windowWidth / 16; bar_3.h = windowHeight; bar_3.x = area_2.x + area_2.w;
SDL_Rect enemy; enemy.w = area_1.w / 3; enemy.h = area_1.w / 3; SDL_Rect cursor; cursor.w = (windowWidth / 8); cursor.h = (windowHeight / 8); //Enemy variables (they're special) int enemies = 30; bool enemy_active[enemies]; int enemy_y[enemies]; int enemy_green[enemies]; int enemy_blue[enemies];
//Game loop while (running) { //Event handling while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { running = false; } if (event.type == SDL_FINGERDOWN) { x = event.tfinger.x * windowWidth - 16; green = event.tfinger.x * 255; y = event.tfinger.y * windowHeight - 16; blue = event.tfinger.y * 255; } if (event.type == SDL_FINGERMOTION) { x += event.tfinger.dx * windowWidth; green = event.tfinger.x * 255; y += event.tfinger.dy * windowHeight; blue = event.tfinger.y * 255; } } square_x = x; square_y = y; square_x = square_x - (square_x % (windowWidth / 8)); square_y = square_y - (square_y % (windowHeight / 8));
//Enemy routine /* for (int i = 0;i < enemies;i ++) {
} */
//Color scaling if (red < 255) { red += 5; }
//Rendering! SDL_FillRect(buffer,&bar_1, SDL_MapRGB(surface->format,255-red,0,0)); SDL_FillRect(buffer,&area_1, SDL_MapRGB(surface->format,red,green,blue)); SDL_FillRect(buffer,&bar_2_1, SDL_MapRGB(surface->format,255-red,0,0)); SDL_FillRect(buffer,&area_2, SDL_MapRGB(surface->format,red,green,blue)); SDL_FillRect(buffer,&bar_3, SDL_MapRGB(surface->format,255-red,0,0)); cursor.x = square_x; cursor.y = square_y; SDL_BlitSurface(buffer,NULL,surface,NULL); SDL_UpdateWindowSurface(window);
//Adding delay for framerate reasons SDL_Delay(16); } //Cleaning up buffer SDL_FreeSurface(buffer);
//Cleaning up the window SDL_DestroyWindow(window);
//Cleaning up SDL SDL_Quit();
//Returning 0 return 0; }
|
|
|
Logged
|
|
|
|
ThemsAllTook
|
|
« Reply #9 on: August 12, 2015, 07:15:02 PM » |
|
Looks like bar_1.x and bar_1.y are uninitialized, so you're probably getting garbage values outside the bounds of your window. All the others seem to be based on the same uninitialized values, so if the first one is offscreen, they all will be.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #10 on: August 13, 2015, 02:06:07 AM » |
|
Oh... I thought those got initialised to zero in the object's constructor... Well, thanks for the help! I'll go fix this incredibly trivial bug with horrible consequences.
|
|
|
Logged
|
|
|
|
Cheezmeister
|
|
« Reply #11 on: August 13, 2015, 06:52:19 PM » |
|
Oh... I thought those got initialised to zero in the object's constructor... Well, thanks for the help!
What is this "object" you speak of? What is a "constructor"? Somewhere, far away, Sam and Bjarne are reading this and stifling giggles.
|
|
|
Logged
|
|
|
|
Cheezmeister
|
|
« Reply #12 on: August 13, 2015, 06:54:22 PM » |
|
By which I mean, SDL is a virgin C library. SDL_Rect is a struct, not a class. It doesn't have a constructor and if it did I doubt it'd initialize anything by default.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #13 on: August 13, 2015, 07:40:17 PM » |
|
Right, I keep forgetting that too, that must be why it's so well made and easy to use. I would have thought some things to be classes though, like surfaces or things like that, but I guess passing a pointer to a function is fine too for simulating member functions.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #14 on: August 18, 2015, 06:50:32 PM » |
|
#include <iostream> #include <math.h> #include "SDL.h"
using namespace std;
const int WindowX = 400; const int WindowY = 250; const int WindowWidth = 600; const int WindowHeight = 400;
double timeRate = 1;
int tile_x(int tileID) { return (tileID % 4) * 16; }
int tile_y(int tileID) { return floor(tileID / 4) * 16; }
int sign(int argument) { if (argument > 0) { return 1; } else if (argument < 0) { return -1; } else { return 0; } }
struct MY_Input { bool quit = false; bool up = false; bool down = false; bool left = false; bool right = false; bool space = false;
void update(SDL_Event* event) { while (SDL_PollEvent(event)) { if (event->type == SDL_QUIT) { quit = true; } else if (event->type == SDL_KEYDOWN) { if (event->key.keysym.sym == SDLK_SPACE) { space = true; } if (event->key.keysym.sym == SDLK_w) { up = true; } if (event->key.keysym.sym == SDLK_s) { down = true; } if (event->key.keysym.sym == SDLK_a) { left = true; } if (event->key.keysym.sym == SDLK_d) { right = true; } } else if (event->type == SDL_KEYUP) { if (event->key.keysym.sym == SDLK_SPACE) { space = false; } if (event->key.keysym.sym == SDLK_w) { up = false; } if (event->key.keysym.sym == SDLK_s) { down = false; } if (event->key.keysym.sym == SDLK_a) { left = false; } if (event->key.keysym.sym == SDLK_d) { right = false; } } } } };
struct MY_Player { double x = 0;//Pixels double y = 0;//Pixels double xSpeed = 0;//Pixels per frame double ySpeed = 0;//Pixels per frame double xSpeedPrev = 0;//Pixels per frame double ySpeedPrev = 0;//Pixels per frame double xMaxSpeed = 4;//Pixels per frame double yMaxSpeed = 6;//Pixels per frame double jumpSpeed = 6;//Pixels per frame double gravity = 0.25;//Pixels per frame squared double acceleration = 0.5;//Pixels per frame squared
//Creating player surface SDL_Surface* playerSurface;
MY_Player() { //Initializing player surface playerSurface = SDL_LoadBMP("resource_images/player.bmp"); } ~MY_Player() { //Freeing player surface SDL_FreeSurface(playerSurface); }
void update(MY_Input* input) { if (input->left && !input->right) { xSpeed -= (acceleration * timeRate); if (xSpeed < -xMaxSpeed) { xSpeed = -xMaxSpeed; } } else if (input->right && !input->left) { xSpeed += (acceleration * timeRate); if (xSpeed > xMaxSpeed) { xSpeed = xMaxSpeed; } } else { xSpeed -= sign(xSpeed) * (acceleration * timeRate); if (abs(xSpeed) < (acceleration * timeRate)) { xSpeed = 0; } } x += (acceleration * timeRate) * ((xSpeedPrev + xSpeed) / 2);
if (input->up && y == 200) { ySpeed = -8; }
ySpeed += (gravity * timeRate); if (ySpeed > yMaxSpeed) { ySpeed = yMaxSpeed; }
y += (acceleration * timeRate) * ((ySpeedPrev + ySpeed) / 2);
if (y > 200) { y = 200; ySpeed = 0; }
xSpeedPrev = xSpeed; ySpeedPrev = ySpeed; } };
struct MY_Renderer { SDL_Rect rect1; SDL_Rect rect2; MY_Renderer() { rect1.x = tile_x(0); rect1.y = tile_y(0); rect1.w = 16; rect1.h = 16; rect2.x = 0; rect2.y = 0; rect2.w = 16; rect2.h = 16; } void draw(SDL_Surface* buffer,MY_Player* player) { //Clear the buffer SDL_FillRect(buffer,NULL,SDL_MapRGB(buffer->format,0,0,0));
//Blit the player tile sprite to the buffer rect2.x = player->x; rect2.y = player->y; SDL_BlitSurface(player->playerSurface,&rect1,buffer,&rect2); } };
int main(int argc, char *argv[]) { //Init SDL SDL_Init(SDL_INIT_EVERYTHING);
//Create window SDL_Window* window = SDL_CreateWindow("Game",WindowX,WindowY,WindowWidth,WindowHeight,SDL_WINDOW_ALLOW_HIGHDPI);
//Getting the window's surface SDL_Surface* windowSurface = SDL_GetWindowSurface(window);
//Creating buffer SDL_Surface* buffer = SDL_CreateRGBSurface(0,WindowWidth,WindowHeight,32,0,0,0,0);
//Creating tile surface SDL_Surface* tile = SDL_LoadBMP("resource_images/tileset_1.bmp");
//Initiating game loop bool running = true; SDL_Event event; MY_Input input; MY_Player player; MY_Renderer renderer; long int ticks = 0;
while (running) { //Update ticks ticks = SDL_GetTicks(); ticks += 16;
//Get input input.update(&event);
//Update player player.update(&input);
//Render everything to the buffer renderer.draw(buffer,&player);
//Blit the buffer to the screen surface SDL_BlitSurface(buffer,NULL,windowSurface,NULL);
//Updating the screen with the window! SDL_UpdateWindowSurface(window);
//Checking if we quit the program if (input.quit) { running = false; }
//Wait one frame if (ticks > SDL_GetTicks()) { SDL_Delay(ticks - SDL_GetTicks()); } }
//Freeing tile surface SDL_FreeSurface(tile);
//Freeing buffer surface SDL_FreeSurface(buffer);
//Destroy window SDL_DestroyWindow(window);
//Quit SDL SDL_Quit(); return 0; }
If anyone is willing to parse through this, I'd like some feedback on my code so far, since I don't usually program in C++. I used to have everything in the main function but I broke down everything in structures and functions. I'd like to know if I did a good job or not hehehe.
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #15 on: August 19, 2015, 01:52:08 AM » |
|
Nice to have it confirmed that STL does lack sign(), as I've too implemented it myself instead. I'd actually never heard of the function in all my years programming in C++ until I saw it in a Unity C# tutorial and now I use it all the time in both languages.
Mine is shorter and looks something like this: return (argument < 0) ? -1 : ((argument > 0) ? 1 : 0);
Also you don't need to use {} if there's only one expression (with a semicolon at the end).
|
|
|
Logged
|
|
|
|
oahda
|
|
« Reply #16 on: August 19, 2015, 01:56:12 AM » |
|
All in all this kind of looks more like a C program than a C++ program with all those structs instead of classes with all public members. Might want to look into that. Also you're still seemingly putting everything into the same file altho you're no longer putting everything in the main function. Might want to split stuff up a bit? Learn how to do classes properly with a header and a source file? You generally put the class definition in a .h or .hpp header file and the implementation in a .cpp source file and then just include the header from where you need the class.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #17 on: August 19, 2015, 09:34:28 AM » |
|
I tried separating everything into source and header files, and now my compiler returns "build failed" with no errors. What the fuck Stroustrup?
Edit: found the bug. Turns out that trying to make variables global in c++ is guaranteed to backfire.
|
|
« Last Edit: August 19, 2015, 10:02:44 AM by ProgramGamer »
|
Logged
|
|
|
|
ThemsAllTook
|
|
« Reply #18 on: August 19, 2015, 10:13:11 AM » |
|
Edit: found the bug. Turns out that trying to make variables global in c++ is guaranteed to backfire.
I'm not sure if there's a more C++ way to do it, but the C way is to declare globals as extern in a header, and put the non-extern definition in the corresponding implementation file. That way they're visible to anything that includes the header, and the linker will resolve them to a single location.
|
|
|
Logged
|
|
|
|
ProgramGamer
|
|
« Reply #19 on: August 19, 2015, 10:50:08 AM » |
|
I did that, that's how I fixed the bug. My problem was that I had constants in there too, so I had to declare them extern in their implementation too.
Edit: I was toying around with trigonometry and weird bugs kept creeping up. Turns out I put a += where an = was supposed to be and the c++ trig functions use radians (I was assuming they used degrees). This is the programming equivalent of forgetting to change the mode of your calculator!
|
|
« Last Edit: August 19, 2015, 05:30:33 PM by ProgramGamer »
|
Logged
|
|
|
|
|