Slow Paint() Performance - c++

I want to display a customized item, basically a colored table with variable column number and width. I'm using c++ Builder XE2 Rad Studio for this.
So, I created a new class inherting from TGraphicControl overwriting void __fastcall Paint(void).
The Problem: it takes quite some time (between 15 and 30 ms) to draw 12 colored rectangles with text, so I expect to have done something wrong. I suspect ShadowRect to do things one could implement better, but I'm not really sure...
Does anyone see my mistake here?
Code to Draw the Paint() Event:
void __fastcall CustomTrgDrawings::Paint(void){
ResetCanvasTools();
ShadowRect(ClientRect,colBG-0x111111,coladdLight,colText,"");
Canvas->TextOutA(5,8,String().sprintf(L"Logic Box %u",fid));
Canvas->Font->Style = TFontStyles() << fsBold;
ShadowRect(nameRec,colBtnBG,coladdLight,colText,fName);
Canvas->Font->Style = TFontStyles() ;
ShadowRect(ch1Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch1Id],true,3,true);
ShadowRect(ch2Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch2Id],true,3,true);
ShadowRect(ch3Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch3Id],true,3,true);
ShadowRect(ch4Rec,colBtnBG-0x110011,coladdLight,colText,channels->Strings[fch4Id],true,3,true);
ShadowRect(norm1Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm1],true,3,true);
ShadowRect(norm2Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm2],true,3,true);
ShadowRect(norm3Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm3],true,3,true);
ShadowRect(norm4Rec,colBtnBG-0x000011,coladdLight,colText,norms->Strings[fnorm4],true,3,true);
ShadowRect(logicRec,colBtnBG-0x002222,coladdLight,colText,logics->Strings[flogicId],true,3,true);
ShadowRect(normOutRec,colBtnBG-0x002200,coladdLight,colText,norms->Strings[fnormOut],true,3,true);
}
void CustomTrgDrawings::ResetCanvasTools(){
Canvas->Brush->Color=clNone;
Canvas->Brush->Style=bsClear;
Canvas->Pen->Color=clNone;
Canvas->Pen->Mode=pmCopy;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Width=1;
Canvas->Font->Color=clBlack;
Canvas->Font->Size=8;
Canvas->Font->Style=TFontStyles();
}
void CustomTrgDrawings::ShadowRect(const TRect pos,const TColor bg, const TColor add,const TColor fg, const String text,const bool shadow,const int align,const bool comboIcon){
int textLen;
int textX,textY;
int iconWidth=0;
Canvas->Brush->Style=bsSolid;
Canvas->Brush->Color=bg;
Canvas->Pen->Color=bg-4*add;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Width=1;
Canvas->FillRect(pos);
if(shadow){
Canvas->Pen->Color=bg-2*add;
Canvas->MoveTo(pos.Left,pos.Bottom-1);
Canvas->LineTo(pos.Right-1,pos.Bottom-1);
Canvas->LineTo(pos.Right-1,pos.Top);
Canvas->Pen->Color=bg+2*add;
Canvas->MoveTo(pos.Right-1,pos.Top);
Canvas->LineTo(pos.Left,pos.Top);
Canvas->LineTo(pos.Left,pos.Bottom-1);
}
if(comboIcon){
iconWidth=6;
Canvas->Pen->Style=psSolid;
Canvas->Pen->Mode=pmMask;
Canvas->Pen->Width=3;
Canvas->Pen->Color=bg-2*add;
Canvas->MoveTo(pos.Right-iconWidth-5,pos.Top+6);
Canvas->LineTo(pos.Right-iconWidth/2-5,pos.Top+10);
Canvas->LineTo(pos.Right-5,pos.Top+6);
}
Canvas->Brush->Style=bsClear;
Canvas->Pen->Color=fg;
textLen=Canvas->TextWidth(text);
switch(align%3){ //horizontal position
case 0: //left
textX=3;
break;
case 1: //middle
textX=((pos.Width()-iconWidth)-textLen)/2;
break;
case 2: //right
textX=(pos.Width()-iconWidth)-textLen;
break;
}
switch(align/3){ //vertical position
case 0: //top
textY=-1;
break;
case 1: //middle
textY=(pos.Height()-Canvas->TextHeight(text))/2;
break;
case 2: //bottom
textY=pos.Height()-Canvas->TextHeight(text);
break;
}
Canvas->TextOutA(pos.Left+textX,pos.Top+textY,text);
}

Related

Infrared selected switch statement using Arduino and Neopixels

I'm having some problems putting the finishing touches on the latest project. The Idea is to have an Infrared receiver mounted on a single Arduino Uno, taking ques from a remote to select preprogramed patterns on a Neopixel strip (selection from the Neopixel Strand test)
Here is my code
//Always comment your code like a violent psychopath will be maintaining it and they know where you live
#include <Adafruit_NeoPixel.h> //Neopixel Library
#include <IRLibAll.h> //IR reciever Library
IRrecvLoop myReceiver(2); //IR receiver on IO pin 2
IRdecode myDecoder; //Decoder object
unsigned long oldCode = 00000000; //Comparison variable to evaluate the execution of the 'Check' function later
Adafruit_NeoPixel strip (3,3,NEO_RGBW + NEO_KHZ800); //Creates the Pixel strip as an object in the code
void setup() {
strip.begin(); //Initialise the Neopixel strip
strip.show(); //Initialise all pixels to 'off'
myReceiver.enableIRIn(); // Start the IR receiver
}
void loop() {
check(); //Run the check function
delay(20); //Delay before running the loop again by 20 milliseconds giving time to recieve signals
}
void check() { //check Function: Checks for an IR signal before nominating which of the test displays to run
if (oldCode = myDecoder.value){ //Evaluates if the IR code recieved from the remote matches 'oldCode' and if it does....
return; //Terminates the check Function returning its values
}
if (myReceiver.getResults()) {
myDecoder.decode();
if (myDecoder.protocolNum == NEC) {
switch(myDecoder.value) { //Activate this switch statement based on the value 'myDecoder' holds
case 0xFFA25D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE21D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF629D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF22DD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF02FD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFC23D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE01F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFA857: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF906F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF9867: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFB04F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF6897:
colorWipe(strip.Color( 0, 0, 0), 50); // Black/off "0"
Serial.println("0 - Black/off");
break;
case 0xFF30CF:
colorWipe(strip.Color(255, 0, 0), 50); // Red "1"
Serial.println("1 - All red");
break;
case 0xFF18E7:
colorWipe(strip.Color( 0, 255, 0), 50); // Green "2"
Serial.println("2 - All green");
break;
case 0xFF7A85:
colorWipe(strip.Color( 0, 0, 255), 50); // Blue "3"
Serial.println("3 - All blue");
break;
case 0xFF10EF:
theaterChase(strip.Color(127, 127, 127), 50); // White "4"
Serial.println("4 - All white");
break;
case 0xFF38C7:
theaterChase(strip.Color(127, 0, 0), 50); // Red "5"
Serial.println("5");
break;
case 0xFF5AA5:
theaterChase(strip.Color( 0, 0, 127), 50); // Blue "6"
Serial.println("6");
break;
case 0xFF42BD:
rainbow(10); // "7"
Serial.println("7");
break;
case 0xFF4AB5:
theaterChaseRainbow(50); // "8"
Serial.println("8");
break;
case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFFFFFFF: Serial.println("Please release button and reselect"); break;
}
}
oldCode = myDecoder.value; //make the new button state equal the old buttonstate preventing the button from activating if statement
}
}
void colorWipe(uint32_t color, int wait) { //Colour wipe test
while(true) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
check();
delay(wait); // Pause for a moment
}
}
}
void theaterChase(uint32_t color, int wait) { //Theatre Chase test
while(true) {
for(int a=0; a<10; a++) { // Repeat 10 times...
for(int b=0; b<3; b++) { // 'b' counts from 0 to 2...
strip.clear(); // Set all pixels in RAM to 0 (off)
for(int c=b; c<strip.numPixels(); c += 3) { // 'c' counts up from 'b' to end of strip in steps of 3...
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
check(); //Run the check function
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
}
}
}
}
void rainbow(int wait) { //Rainbow test function
while(true) { //while this function is active
for(long firstPixelHue = 0; firstPixelHue < 3*65536; firstPixelHue += 256) { //Sets differing colours for the rainbow
for(int i=0; i<strip.numPixels(); i++) { //For each pixel in strip...
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels()); //balances the colour pattern along the length of the strip
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue))); //run the colour pattern along the strip
check(); //run the check function
}
strip.show(); //Update strip with new contents
delay(wait); //Pause for a moment
}
}
}
void theaterChaseRainbow(int wait) {
while(true) {
int firstPixelHue = 0; //First pixel starts at red
for(int a=0; a<30; a++) { //Repeat 30 times...
for(int b=0; b<3; b++) { //'b' counts from 0 to 2...
strip.clear(); //Set all pixels to off
for(int c=b; c<strip.numPixels(); c += 3) { //'c' counts up from 'b' to end of strip in increments of 3, hue of pixel 'c' is offset by an amount to make one full revolution of the color wheel (range 65536) along the length of the strip (strip.numPixels() steps):
int hue = firstPixelHue + c * 65536L / strip.numPixels(); //create the hue variable and balance the rainbow colouring across the strip
uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
check(); //Run the check function
}
strip.show(); // Update strip with new contents
delay(wait); // Pause for a moment
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
}
}
}
The Problems
I have tried to write a loop that uses a class call to decide what is going on ("check"), I have also written another version where the switch statement is place in the loop instead. The problem from what I understand is that the IR input cannot receive whilst the loop is running (as it cannot interrupt) yet the test patterns will not play unless the loop is running. I have looked at solutions that involve using two Arduino's with either one running IR and the Neopixel respectively but unfortunately this will not match up with my brief - I need to have it running on one Arduino.
Any ideas?
For those who are interested - the final code to this project was...
//Always comment your code like it will be maintained by a violent psychopath who knows where you live.
#include <IRLibAll.h> //Infra Red Library
#include <Adafruit_NeoPixel.h> //Adafruit NeoPixel Library
//======== Constants =============
const int LEDpin = 3; //IO pin for the LED strip
const int LEDcount = 3; //Number of LED's in the strip
const int IRreceiver = 2; //IO pin for the IRreceiver
IRrecvPCI myReceiver(IRreceiver); //Instantiate the Infra Red receiver
IRdecode myDecoder; //Instatiate a Decoder object (Veriable to hold the recieved data from the button press)
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, color_WIPE, SCANNER, FADE }; //Limit the results 'pattern' will accept with an enumeration
//======== Variables =============
//=====Classes and Functions =====
class neoPatterns : public Adafruit_NeoPixel //A class to govern the operation of the Neopixel patterns outside of the Main loop
{
private:
int steps;
uint32_t color;
public:
pattern activePattern; //Tracks the pattern that is currently active on the strip
unsigned long interval; //Milliseconds between updates
unsigned long lastUpdate; //Records the millisecond of the last update
uint32_t color1, color2; //Variables for recording active colors
uint16_t totalSteps; //How many steps of the pattern have been called
uint16_t index; //What step within the pattern we are on
void (*onComplete)(); //onComplete callback function - still wondering how much i need this?
neoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) //Class constructor to...
:Adafruit_NeoPixel(pixels, pin, type) //initialise the Neopixel strip
{
onComplete = callback;
}
void update() //Function that manages updating the pattern
{
if((millis() - lastUpdate) > interval) //Is it time to update?
{
lastUpdate = millis(); //Updates 'lastUpdate' to the current milli's value
switch(activePattern) //Switch statement to track which pattern needs its update function
{
case RAINBOW_CYCLE: //If rainbowCycle...
rainbowCycleUpdate(); //update rainbowCycle
break;
case THEATER_CHASE: //If theatreChase...
theaterChaseUpdate(); //update theatreChase
break;
case color_WIPE: //if colorWipe
colorWipeUpdate(); //update colorWipe
break;
case SCANNER: //if scanner
scannerUpdate(); //update scanner
break;
case FADE: //if fade
fadeUpdate(); //update fade
break;
default:
break;
}
}
}
void increment() //Function for incrementing values to drive strand tests
{
index++; //increment index variable
if (index >= totalSteps) //if index is greater than or equal to totalsteps...
{
index = 0; //..reset index to 0 and...
if (onComplete != NULL) //... if onComplete has no value...
{
onComplete(); //...call the onComplete callback
}
}
}
void rainbowCycle(uint8_t interval) //Rainbow Cycle strand test pattern
{
activePattern = RAINBOW_CYCLE; //Set current active pattern to Rainbow Cycle...
interval = interval; //reset interval to interval
totalSteps = 255; //set total step variable to 255
index = 0; //set index variable to 0
}
void rainbowCycleUpdate() //update for Rainbow Cycle
{
for(int i=0; i< numPixels(); i++) //create a variable called 'i' which is equal to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
setPixelColor(i, wheel(((i * 256 / numPixels()) + index) & 255)); //set the pixel color to ...
}
show(); //update the orders to the Neopixel strand
increment(); //Run the increment function
}
void colorWipe (uint32_t color, uint8_t interval) //color wipe funtion
{
activePattern = color_WIPE; //update the current active pattern to color Wipe
interval = interval; //reset the interval variable
totalSteps = 255; //set the total steps variable to 255
color1 = color; //set color to color 1
index = 0; //reset the index variable to 0
}
void colorWipeUpdate() //Color wipe update function
{
setPixelColor(index, color1); //change the pixel color to color1
show(); //update the strand
increment(); //run the increment function
}
void theaterChase(uint32_t color1, uint32_t color2, uint8_t interval) //Theatre Chase funtion
{
activePattern = THEATER_CHASE; //change the current active pattern to Theatre Chase
interval = interval; //reset the interval variable
totalSteps = numPixels(); //update the total steps variable to be equivilent to the number of pixels
color1 = color1; //Reset color1
color2 = color2; //Reset color2
index = 0; //Set index variable to 0
}
void theaterChaseUpdate() //Theatre Chase update function
{
for(int i=0; i< numPixels(); i++) //take the i variable and reset it to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
if ((i + index) % 3 == 0) //if the total of I and index divide equally by 3...
{
setPixelColor(i, color1); //...set the pixelcolor to color 1...
}
else //...otherwise...
{
setPixelColor(i, color2); //set the pixel color to color 2
}
}
show(); //update the neopixel strand
increment(); //run the increment function
}
void scanner(uint32_t color1, uint8_t interval) //Scanner function
{
activePattern = SCANNER; //update the active pattern to Scanner
interval = interval; //reset the interval variable
totalSteps = (numPixels() - 1) * 2; //set the total steps variable to by equal to twice that of the number of pixels on the strand less one
color1 = color1; //reset the color1 variable
index = 0; //set the index variable to 0
}
void scannerUpdate() //Scanner update function
{
for (int i = 0; i < numPixels(); i++) //take the i variable and reset it to 0 and do loops, whilst the number of pixels in the strip is greater than i, incremeting i every loop.
{
if (i == index) //if the i variable is equivilant to the index variable...
{
setPixelColor(i, color1); //set the pixel color to color1
}
else if (i == totalSteps - index) //if the i variable is equivilant to totalsteps less the value of index...
{
setPixelColor(i, color1); //set the pixel color to color1...
}
else //otherwise...
{
setPixelColor(i, DimColor(getPixelColor(i))); //dim the current pixel value
}
}
show(); //update the strand
increment(); //run the increment function
}
void fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval) //Fade function
{
activePattern = FADE; //set the current active pattern to fade
interval = interval; //reset the interval variable
totalSteps = steps; //create a new steps variable and set it to be eqivilant to totalSteps
color1 = color1; //reset color1
color2 = color2; //reset color2
index = 0; //set index to 0
}
void fadeUpdate() //Fade update function
{
uint8_t red = ((Red(color1) * (totalSteps - index)) + (Red(color2) * index)) / totalSteps;
uint8_t green = ((Green(color1) * (totalSteps - index)) + (Green(color2) * index)) / totalSteps;
uint8_t blue = ((Blue(color1) * (totalSteps - index)) + (Blue(color2) * index)) / totalSteps;
colorSet(Color(red, green, blue));
show(); //update the strand
increment(); //run the increment function
}
uint8_t Red(uint32_t color) //Red color function
{
return (color >> 16) & 0xFF;
}
uint8_t Green(uint32_t color) //Green color function
{
return (color >> 8) & 0xFF;
}
uint8_t Blue(uint32_t color) //Blue color function
{
return color & 0xFF;
}
uint32_t DimColor(uint32_t color) //color dimming function
{
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}
uint32_t wheel(byte wheelPos) //color wheeling function for the rainbow color functions
{
wheelPos = 255 - wheelPos;
if(wheelPos < 85)
{
return Color(255 - wheelPos * 3, 0, wheelPos * 3);
}
else if(wheelPos < 170)
{
wheelPos -= 85;
return Color(0, wheelPos * 3, 255 - wheelPos * 3);
}
else
{
wheelPos -= 170;
return Color(wheelPos * 3, 255 - wheelPos * 3, 0);
}
}
void colorSet(uint32_t color) //color set function sets all colors to the same synchronus color
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}
void IRSelector() //Infra Red selection function - takes action based on IR code received
{
if (myDecoder.protocolNum == NEC) { //ignore any code that is not recieved from a NEC remote control
switch(myDecoder.value) //Switch statement that makes a decision based upon the value recieved from the Infra Red decoder
{
case 0xFFA25D: Serial.println("Untethered button, please select from 0-8"); break; //=====================================================================
case 0xFFE21D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF629D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF22DD: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF02FD: Serial.println("Untethered button, please select from 0-8"); break; // ------------- UNASSIGNED BUTTON SELECTIONS -------------------------
case 0xFFC23D: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFE01F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFA857: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF906F: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFF9867: Serial.println("Untethered button, please select from 0-8"); break;
case 0xFFB04F: Serial.println("Untethered button, please select from 0-8"); break; //=====================================================================
case 0xFF6897: //"0 - All black (off)"
colorWipe(color, interval);
Serial.println("0 - Black/off");
break;
case 0xFF30CF: //"1 - All red"
colorWipe(color, interval);
Serial.println("1 - All red");
break;
case 0xFF18E7: //"2 - All green"
colorWipe(color, interval);
Serial.println("2 - All green");
break;
case 0xFF7A85: //"3 - All blue"
colorWipe(color, interval);
Serial.println("3 - All blue");
break;
case 0xFF10EF: //"4 - All white"
colorWipe(color, interval);
Serial.println("4 - All white");
break;
case 0xFF38C7: //"5 - Rainbow Cycle"
rainbowCycle(interval);
Serial.println("5");
break;
case 0xFF5AA5: //"6 - Theater Chase"
theaterChase(color1, color2, interval);
Serial.println("6");
break;
case 0xFF42BD: //"7 - Scanner"
scanner(color1, interval);
Serial.println("7");
break;
case 0xFF4AB5: //"8 - Fader"
fade(color1, color2, steps, interval);
Serial.println("8");
break;
case 0xFF52AD: Serial.println("Untethered button, please select from 0-8"); break; //button 9 - unassigned
case 0xFFFFFFFF: Serial.println("Please release button and reselect"); break; //consistant repeat code
default:
Serial.print(" other button ");
Serial.println(myDecoder.value);
}//End of Switch
}
}//End of IRSelector method
}; // End of neoPatterns class
void strandComplete();
neoPatterns strand(LEDcount, LEDpin, NEO_RGBW + NEO_KHZ800, &strandComplete); //Neopattern object to define the strand
void setup(){ /*----( SETUP: RUNS ONCE )----*/
Serial.begin(9600); //engage the serial monitor
Serial.println("IR Receiver Button Decode"); //print out to the monitor
myReceiver.enableIRIn(); //Start the receiver
strand.begin(); //start the Neopixel strip
}/*--(end setup )---*/
void loop(){ /*----( LOOP: RUNS CONSTANTLY )----*/
if (myReceiver.getResults()) //check to see if we have received an IR signal?
{
myDecoder.decode(); //Decode the recieved signal
strand.IRSelector(); //Run the IR selection function
myReceiver.enableIRIn(); //reset the receiver for a new code
}
strand.update(); //Run the update function on the Neopixel strand
}/* --(end main loop )-- */
void strandComplete()
{
// Random color change for next scan
strand.color1 = strand.wheel(random(255));
}`

SDL stiff movement

i am making a pong game, lPad is left pad and rPad is the right pad, but i have a problem when any pad is moving up and when i release the up button and press down the pad stops for a while and then goes down, the other thing is i can't move both pads when pressing both controls(only one is moving) with this setup:
if(e.type == SDL_KEYDOWN) {
switch(e.key.keysym.sym) {
case SDLK_s:
lPad.y += 8;
if(lPad.y >= s.SCREEN_HEIGHT - lPad.getHeight()) {
lPad.y = s.SCREEN_HEIGHT - lPad.getHeight();
}
break;
case SDLK_w:
lPad.y -= 8;
if(lPad.y <= 0) {
lPad.y = 0;
}
break;
case SDLK_DOWN:
rPad.y += 8;
if(rPad.y >= s.SCREEN_HEIGHT - rPad.getHeight()) {
rPad.y = s.SCREEN_HEIGHT - rPad.getHeight();
}
break;
case SDLK_UP:
rPad.y -= 8;
if(rPad.y <= 0) {
rPad.y = 0;
}
break;
default:
break;
}
}
Any idea how to fix this and make it smooth ?
It's better to use SDL_GetKeyboardState(NULL) as the function to get input. This way, you can get the entire state of the keyboard simultaneously and thus allow for parallel inputs. If you use the while loop, each event will get caught individually, and thus be choppy.
Here is some sample code on how to use it:
const auto * keys = SDL_GetKeyboardState(NULL);
while(!done) {
while(SDL_PollEvent(&e)) {
if(e.type == SDL_QUIT) {
done = true;
}
}
SDL_PumpEvents();
if (keys[SDL_SCANCODE_UP]) {
p1.set_speed(0, -60000 * delta.count());
}
if (keys[SDL_SCANCODE_DOWN]) {
p1.set_speed(0, 60000 * delta.count());
}
if (keys[SDL_SCANCODE_LEFT]) {
p1.set_speed(-60000 * delta.count(), 0);
}
if (keys[SDL_SCANCODE_RIGHT]) {
p1.set_speed(60000 * delta.count(), 0);
}
Also, might I suggest having a speed variable? Using pixels is not a good way to scale movement, as it depends on the resolution of the screen. Using something based on a time step is much more robust.

Object movement in a 2D Array/Grid

I am trying to create a variation of the classic Snake game.
Basically what i am stuck on is how to restrict the snake movement to a 2D array whereby there will be a 20x20 grid.
At the moment, my snake head which is just a shape drawn with a midpoint, moves freely one pixel at a time within the game board. I require the snake to move one grid space at a time.
This is my snake code:
void Snake::move()
{
switch(direction_){
case Direction::North:
position_.y += 1;
break;
case Direction::East:
position_.x += 1;
break;
case Direction::South:
position_.y -= 1;
break;
case Direction::West:
position_.x -= 1;
}
if (position_.x < 6.4) position_.x = 44.8; else if (position_.x > 44.8) position_.x = 6.4;
if (position_.y < 0) position_.y = 38.4; else if (position_.y > 38.4) position_.y = 0;
}
void Snake::render(prg::Canvas& canvas) const
{
canvas.drawCircle(getPosition().x * 20, getPosition().y * 20,19.2,prg::Colour::GREEN);
}
void Snake::changeDirection(Direction new_direction)
{
direction_ = new_direction;
}
This is the code that handles keyboard input/movement
PlayerSnake::PlayerSnake()
{
prg::application.addKeyListener(*this);
}
PlayerSnake::~PlayerSnake()
{
prg::application.removeKeyListener(*this);
}
bool PlayerSnake::onKey(const prg::IKeyEvent::KeyEvent& key_event)
{
if(key_event.key_state == KeyEvent::KB_DOWN) {
switch(key_event.key) {
case KeyEvent::KB_LEFT_KEY:
changeDirection(Direction::West);
break;
case KeyEvent::KB_RIGHT_KEY:
changeDirection(Direction::East);
break;
case KeyEvent::KB_UP_KEY:
changeDirection(Direction::North);
break;
case KeyEvent::KB_DOWN_KEY:
changeDirection(Direction::South);
break;
}
}
return true;
}
I'm in desperate need of any suggestions and have been racking my brain trying to get the snake to move along a grid. I'm also new-ish to C++ so please understand.
Thanks :)
Don't think of the screen as the data. The screen is a representation of the data. This means that you have to figure out a way to map the data to it's visual equivalent.
If the grid is 20x20, but the screen rendering is 200x200, this implies a 1:10 ratio of pixels to cells. So, one possible method of drawing might look like this. (Sorry, using Processing syntax.)
In processing, one method of drawing a rectangle is using the command rect(int left, int top, int right, int bottom);
So, one implementation might look like this:
void draw_square(int cellx, int celly)
{
rect(cellx*10, celly*10, cellx*10+10, celly*10+10);
}

Linker Issue using Extern

So I have a 2 games running side by side.
proj2/main.cpp (main that gets run that then can call either game)
proj2/spades/display.cpp
proj2/spades/gameplay.cpp
proj2/spades/otherFiles.cpp
proj2/hearts/display.cpp (this is the same display class as in the spades game with the same functions just sightly altered.)
proj2/hearts/hearts.cpp
proj2/hearts/otherFiles.cpp
I'm getting the following linker error,
./gamePlay.o: In function `__static_initialization_and_destruction_0(int, int)':
gamePlay.cpp:(.text+0x396): undefined reference to `spades::display::display()'
./gamePlay.o: In function `__tcf_3':
gamePlay.cpp:(.text+0x480): undefined reference to `spades::display::~display()'
in my gamePlay.cpp file I define the following and I think this is where my error is,
display monitor;
if I add the extern identifier to the line such as
extern display monitor;
the error will disappear only to produce the following issue,
./gamePlay.o: In function `spades::gamePlay::storeBid(std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >&)':
gamePlay.cpp:(.text+0x14ee): undefined reference to `spades::monitor'
gamePlay.cpp:(.text+0x14f3): undefined reference to `spades::display::drawBox(int, int, int, int, int)'
I think this error is this coming from these following lines, but I have no idea how to fix this error. Feeling quite lost at the moment. (I don't understand why I would need the extern as an identifier for monitor either). Also all the includes should be alright.
int key = monitor.captureInput();
monitor.drawBox(2, 5, 3, 2, 0);
Below is my display.h file
/*
* Description: Defines constants and class prototypes and variables
*/
// These are include files they are used for accessing functions and
// variables in external libraries.
#include <ncursesw/ncurses.h>
#include <iostream>
#include <cstdio>
// This is the namespace std, without this you would need to add "std::"
// infront of every variable and function from the std library.
//using namespace std;
// These Mouse Mask Definitions are for some of the common mouse buttons
// that are used in this project.
#define LEFT_UP 1
#define LEFT_DOWN 2
#define LEFT_CLICK 4
#define LEFT_DOUBLECLICK 24
#define RIGHT_UP 4096
#define RIGHT_DOWN 8192
#define RIGHT_CLICK 16384
#define RIGHT_DOUBLECLICK 32768
#define MIDDLE_ROLLUP 1048576
#define MIDDLE_ROLLDOWN 256
namespace spades {
// Some extended character definitions for showing the special symbols
// in the extended UTF-8 character map.
const char joker[] = {0xE2, 0x98, 0xBA, 0};
const char clubs[] = {0xE2, 0x99, 0xA3, 0};
const char diamonds[] = {0xE2, 0x99, 0xA6, 0};
const char hearts[] = {0xE2, 0x99, 0xA5, 0};
const char spades[] = {0xE2, 0x99, 0xA0, 0};
/*
* This is the display class definitions
*/
class display {
public:
/* "constructor"
* This function is called whenever a new object of class display is first
* created. Put initialization functions in here.
*/
display(void){};
/* "destructor"
* This function is called just before a object is deleted. Put clean up
* functions in here.
*/
~display(){}; // destructor
// captures user input
int captureInput(void);
// stores new screensize on update
void handleResize(int sig);
/*
* Drawing commands
*/
// display a card on the screen
void displayCard(int x, int y, int suit, int number, int printAtt);
// erase in the shape of a box on the screen
void eraseBox(int x, int y, int sizeX, int sizeY);
// draw a box on the screen
void drawBox(int x, int y, int sizeX, int sizeY, int printAtt);
// display banner text at the bottom of the screen
void bannerBottom(std::string bannerText);
void bannerAboveBottom(std::string bannerText);
// display banner text at the top of the screen
void bannerTop(std::string bannerText);
// get information about the display
int getCols(void) {return cols;}
int getLines(void) {return lines;}
int getMouseEventX(void) {return mouseEventX;}
int getMouseEventY(void) {return mouseEventY;}
int getMouseEventButton(void) {return mouseEventButton;}
// Updates the screen after you finish drawing
void updateScreen(void) {refresh();}
// sets an offset for when cards clip the bottom of the screen
void setBottomOffset(int offset) {lineBoundaryOffset=offset;}
private:
// These are private functions and variables used inside of display.
// You should not try to access these from outside the display class.
void printFace(int suit, int number, int line, int printAtt);
void printSuit(int suit);
void printNumber(int number);
int cols;
int lines;
int mouseEventX;
int mouseEventY;
int mouseEventButton;
int lineBoundaryOffset;
};
}
Below is my display.cpp file
/*
* Description: Defines the functionality of the user interface.
*
* NOTES:
* * Requires the terminal (Putty) to be set to UTF-8.
* * Does not function when running a screen session.
*/
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include "display.h"
namespace spades {
//using namespace std;
/* Function: This is the constructor.
* Description: It is called whenever an object of class display is initialized
*/
display::display(void) {
/* Initilization of Terminal */
// required to get card suits displaying, combined with UTF-8 set in terminal
setlocale(LC_ALL,"en_US.utf-8");
// initialize ncurses screen
initscr();
// allow for color settings
start_color();
// clear the screen
clear();
// turn off the keyboard echo (reqiured while drawing)
noecho();
// Change to character mode (so individual characters are being read at a
// time rather than waiting for a carriage return).
cbreak();
// Allows for function keys to be used (also nessacary for getting the mouse
// movement working).
keypad(stdscr, TRUE);
// set which mouse events are captured
mousemask(ALL_MOUSE_EVENTS, NULL);
// Setting the timeout for the capture input values are in 1/10ths of a second.
halfdelay(5);
// setup the screen size settings.
cols = 80;
lines = 24;
// this updates the locally stored col and line variables
handleResize(0);
// set a no card draw offset of 1 so the bottom banner is not overwritten
lineBoundaryOffset = 1;
// Settings for card colors (these can be set outside of the display class)
init_pair(1, COLOR_CYAN, COLOR_BLACK); // for card outline
init_pair(2, COLOR_BLUE, COLOR_BLACK); // for spades and clubs
init_pair(3, COLOR_RED, COLOR_BLACK); // for hearts and diamonds
init_pair(4, COLOR_GREEN, COLOR_BLACK); // for turned over card
init_pair(5, COLOR_GREEN, COLOR_BLACK); // for box drawing
init_pair(6, COLOR_GREEN, COLOR_BLACK); // for banner display
}
/* Function: This is the destructor.
* Description: This is called just before an object is deleted.
*/
display::~display() {
// this is turns off all the special settings and returns the terminal to normal
endwin();
// insert deletion of dynamically created objects here too
}
/*
* Function: This captures all the userinput.
* Description: It captures mouse and keyboard events.
* Returns "Positive Number"
* - for user keypress
* - this is a character code typed
* Returns "0" - for no user input
* - this is when nothing is typed for half a second
* - allows for other timed operations to occur
* Returns "-1" - for mouse event
* - details of the mouse event must be fetched from this class
* - use getMouseEventX, getMouseEventY and getMouseEventButton
*/
int display::captureInput(void) {
// obtain one mouse event or keypress
int ch=getch();
// this is a switch statement for the result of getch
switch (ch) {
case KEY_MOUSE: // this occurs when an mouse event occurs
{
MEVENT mevent; // this is a variable declared of type MEVENT
getmouse(&mevent); // this gets the mouse event from ncurses (library)
mouseEventX = mevent.x; // get the column location of the event
mouseEventY = mevent.y; // get the row location of the event
mouseEventButton = mevent.bstate; // get the button state of the mouse
return -1; // -1 is for a mouse event
}
break;
case ERR: // this occurs when there is a timeout
{
return 0; // 0 is when nothing occurs
}
break;
default: // this occurs when a key is pressed
return ch; // a character is when the user types something
}
return 0; // this is never called, but is used to stop compiler complaints
}
/*
* Function: Updates all the information in the display class on window resize
* Description: This function just updates information, it requires the call
* from a static singal handler. Signal handlers service interrupts and require
* a static function to be called, because they are not part of the main
* program control flow. The signal handler should be declared in the main
* class, because your game should redraw the display on a resize.
*/
void display::handleResize(int sig) {
#ifdef TIOCGSIZE // operating system dependant differences
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts); // get the information of the terminal
cols = ts.ts_cols;
lines = ts.ts_lines;
#elif defined(TIOCGWINSZ)
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); // get the information of the terminal
cols = ts.ws_col;
lines = ts.ws_row;
#endif /* TIOCGSIZE */
resizeterm(lines, cols); // sets the ncurses window size correctly
}
/*
* Function: Displays various cards on the game screen
* Description: This function displays various playing cards on the screen.
* The first two arguments are the x and y coordinates of the top left corner
* of the card.
* The suit values are: 1=spades, 2=hearts, 3=clubs, 4=diamonds
* The numbers are: 1=Ace, 2-10=2-10, 11=Jack, 12=Queen, 13=King, 14=Joker
* Any suit and number that do not match the valid numberrs generates a face down
* card.
* The printAtt allows for one or more any of the following display settings:
* A_NORMAL Normal display (no highlight)
* A_STANDOUT Best highlighting mode of the terminal.
* A_UNDERLINE Underlining
* A_REVERSE Reverse video
* A_BLINK Blinking
* A_DIM Half bright
* A_BOLD Extra bright or bold
* A_PROTECT Protected mode
* A_INVIS Invisible or blank mode
* A_ALTCHARSET Alternate character set
* A_CHARTEXT Bit-mask to extract a character
* COLOR_PAIR(n) Color-pair number n
*/
void display::displayCard(int x, int y, int suit, int number, int printAtt) {
// Ncurses drawing settings
attron(COLOR_PAIR(1) | printAtt);
// prevent draw if it off the screen
if (x>=0 && y>=0 && x<cols-6 && y<lines-lineBoundaryOffset) {
// print the top lines of the card
mvprintw(y,x,"\u250c\u2500\u2500\u2500\u2500\u2510");
// the next 4 if statements prevent draw if it is over the bottom of the screen
if (y<lines-1-lineBoundaryOffset) {
move(y+1,x); // move command
printFace(suit,number,0, printAtt); // call function to print card face
}
if (y<lines-2-lineBoundaryOffset) {
move(y+2,x); // move command
printFace(suit,number,1, printAtt); // call function to print card face
}
if (y<lines-3-lineBoundaryOffset) {
move(y+3,x); // move command
printFace(suit,number,2, printAtt); // call function to print card face
}
if (y<lines-4-lineBoundaryOffset) {
// prints the bottom lines of the card
mvprintw(y+4,x,"\u2514\u2500\u2500\u2500\u2500\u2518");
}
}
// Ncurses turn off the drawing settings
attroff(COLOR_PAIR(1) | printAtt);
}
/*
* Function: Print a single line of what is written on the card.
* Description: This copies suit, number and printAtt from the calling function.
* Also includes what line of the card face is being drawn.
*/
void display::printFace(int suit, int number, int line, int printAtt) {
// draw left edge of the card
printw("\u2502");
if (suit==2 || suit==4) { // Red for Hearts and Diamonds
attron(COLOR_PAIR(3) | printAtt);
} else { // Black for Spades and Clover
attron(COLOR_PAIR(2) | printAtt);
}
// this the display of the joker
if (number==14) {
if (line==0)
printw("J%s ", joker);
if (line==1)
printw("oker");
if (line==2)
printw(" J%s", joker);
// this is the display for the cards with suits and numbers
} else if (suit>=1 && suit <=4 && number>=1 && number<=13) {
if (line==0) {
printSuit(suit); // function to draw suit
printNumber(number); // function to draw number
if (number!=10)
printw(" ");
printw(" ");
} else if (line==2) {
if (number!=10)
printw(" ");
printw(" ");
printNumber(number); // function to draw number
printSuit(suit); // function to draw suit
} else {
printw(" ");
}
// this is for a face down card
} else {
// the face down cards have a special color
attron(COLOR_PAIR(4) | printAtt);
if (line==0)
printw("%s %s", spades, hearts);
if (line==1)
printw("Play");
if (line==2)
printw("%s %s", diamonds, clubs);
attroff(COLOR_PAIR(1) | printAtt);
}
// turn on the card edge color settings
attron(COLOR_PAIR(1) | printAtt);
// print the right edge of the card
printw("\u2502");
}
/*
* Function: Print the suit of the card
* Description: This is just a look up table.
*/
void display::printSuit(int suit) {
switch (suit) {
case 1:
printw("%s",spades);
break;
case 2:
printw("%s",hearts);
break;
case 3:
printw("%s",clubs);
break;
case 4:
printw("%s",diamonds);
break;
default:
printw(" ");
break;
}
}
/*
* Function: Prints the number on the card
* Description: This is just a look up table.
*/
void display::printNumber(int number) {
switch (number) {
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
printw("%i",number);
break;
case 1:
printw("%c",'A');
break;
case 11:
printw("%c",'J');
break;
case 12:
printw("%c",'Q');
break;
case 13:
printw("%c",'K');
break;
default:
printw(" ");
break;
}
}
/*
* Function: Erases a rectangle on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is.
*/
void display::eraseBox(int x, int y, int sizeX, int sizeY) {
std::string strDraw;
int yCount;
int maxSizeX;
// this limits the column size of the draw when it is over the edge
// of the drawing area
if (sizeX+x > cols)
maxSizeX=cols-x;
else
maxSizeX=sizeX;
// for the number of rows that need to be drawn
for (yCount=0; yCount<sizeY;yCount++) {
// if the box goes over the edge of the drawable screen
// stop drawing by breaking the loop
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// reset the line to be drawn
strDraw = "";
// check that x is not off the screen
if (x<=cols && x >= 0) {
// make a string needed for box width
strDraw.append(maxSizeX,' ');
// print the line of the box
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
}
/*
* Function: Draws a box on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is. printAtt allows for changes in the
* display settings.
*/
void display::drawBox(int x, int y, int sizeX, int sizeY, int printAtt) {
std::string strDraw;
int ii;
int yCount;
// set the box setting colors on
attron(COLOR_PAIR(5) | printAtt);
// for the box height being drawn loop
for (yCount=0; yCount<sizeY;yCount++) {
// break loop if the drawing is offscreen
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// if x is on the screen
if (x<=cols) {
strDraw = "";
// for the box width loop
for (ii=0;ii<sizeX;ii++){
// stop drawing if the x is offscreen
if (ii+x > cols || x < 0)
break;
// first line
if (yCount==0) {
if (ii==0) {
strDraw.append("\u250c"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2510"); // right
} else {
strDraw.append("\u2500"); // middle
}
// last line
} else if (yCount==sizeY-1) {
if (ii==0) {
strDraw.append("\u2514"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2518"); // right
} else {
strDraw.append("\u2500"); // middle
}
// other lines
} else {
if (ii==0) {
strDraw.append("\u2502"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2502"); // right
} else {
strDraw.append(" "); // middle
}
}
}
// print the line that was created
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
// turn off the attribute colors
attroff(COLOR_PAIR(5) | printAtt);
}
/*
* Function: Draws a banner of text at the bottom right of the screen
* Description: Inverts the color and draws the banner at the bottom
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-1,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-1,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-1,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
void display::bannerAboveBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-2,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-2,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-2,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
}
/*
* Function: Draws a banner of text at the top left of the screen
* Description: Inverts the color and draws the banner at the top
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerTop(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(0,0);
// prints out the banner text
printw("%s", bannerText.c_str());
// fill in extra space after the banner text
hline(' ',cols - bannerText.size());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(0,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
}
Below is my gameplay.h file
#include "player.h"
#include "display.h"
#ifndef gamePlay_H
#define gamePlay_H
namespace spades {
class gamePlay{
bool spadesBroken;
public:
vector <spades::card> getDeck();
vector <spades::player>getplayers();
bool getSpadesBroken() {return spadesBroken;}
void setSpadesBroken(bool b){spadesBroken = b;}
int compareCenter(int leadplayer);
void createDeck();
void deal(vector <spades::card> &deck, vector <spades::player> &players);
void handSort();
bool containSuit(spades::card lead, spades::player players);
bool onlySpade(spades::player play);
int handCheck(int xevent, int yevent, vector <spades::player> players, int trickStart);
void displayHand();
void displayAdd();
void humanPlay(int trickStart);
void CPUplay(int trickStart, int CPU);
void score(spades::player &play, spades::player &play2);
void storeBid(stringstream &msg);
void runGame();
};
}
#endif
Below is my gameplay.cpp file
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include <fstream>
#include "gamePlay.h"
#include <cstdlib>
#include <sstream>
#include <ctime>
namespace spades {
vector <spades::player> players(4);
vector <spades::card> deck(52);
display monitor;
card center[4];
vector <spades::card> gamePlay::getDeck(){return deck;}
vector <spades::player> gamePlay::getplayers(){return players;}
//sorts the cards in the players hand into diamonds, clubs, hearts, spades
void gamePlay::handSort(){
int spades[13];
int hearts[13];
int clubs[13];
int diamonds[13];
int index;
int i;
for(i=0; i<13; i++){ //determines the card number and places them into corresponding indexes
index = (players.at(0).hand.at(i).getCardNum()+11)%13; //cause the cards to be placed based on their number with 2 being index 0 and 1(ace) being index 12
switch(players.at(0).hand.at(i).getSuit())
{
case 1: spades[index] = 1;
break;
case 2: hearts[index] = 1;
break;
case 3: clubs[index] = 1;
break;
case 4: diamonds[index] = 1;
break;
default: mvprintw(3,2,"!!!!!!!we have a problem!!!!!!!!!!");
break;
}
}
i = 0;
while(i<13){ //being placing the ordered cards back into the players hand
int j = 0;
while(j < 13){
if(diamonds[j] == 1){ //if a card has been placed in this index for the diamonds only array
if(j+2 == 14) //if the card is an ace
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2); //add 2 to each index to get the card number
players.at(0).hand.at(i).setSuit(4);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(clubs[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(3);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(hearts[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(2);
i++;
}
j++;
}
j = 0;
while(j < 13){
if(spades[j] == 1){
if(j+2 == 14)
players.at(0).hand.at(i).setCardNum(1);
else
players.at(0).hand.at(i).setCardNum(j+2);
players.at(0).hand.at(i).setSuit(1);
i++;
}
j++;
}
}
}
//compares the center pile of 4 played cards to determine who wins the trick
int gamePlay::compareCenter(int leadplayer){
int highest = center[leadplayer].getCardNum();
if(center[leadplayer].getCardNum() == 1)
highest = 14;
int suit = center[leadplayer].getSuit();
int player = leadplayer;
for(int i = leadplayer+1; i < leadplayer+4; i++)
{
if(center[i%4].getSuit() == 1)
setSpadesBroken(true);
if((suit != 1) && (center[i%4].getSuit() == 1))
{
player = i%4;
suit = 1;
highest = center[i%4].getCardNum();
}
if(suit == center[i%4].getSuit()){
if(center[i%4].getCardNum() == 1){
player = i % 4;
highest = 14;
}
if(highest < center[i%4].getCardNum())
{
player = i%4;
highest = center[i%4].getCardNum();
}
}
}
players.at(player).setTricksTaken(players.at(player).getTricksTaken()+1); //increments the trick count of the winning player
return player; //return the player who won to determine who goes first next turn
}
//Create the deck of 52 cards by setting the suit and number of each card to a nonzero integer
void gamePlay::createDeck() {
for(int j = 0; j < 52; j++)
{
deck.at(j).setCardNum((j%13)+1);
deck.at(j).setSuit((j/13)+1);
}
random_shuffle(deck.begin(), deck.end());
}
//deal out 13 cards to each player by setting the
void gamePlay::deal(vector <spades::card> &newdeck, vector <spades::player> &newplayers){
for(int i = 0; i<52; i++){
newplayers.at(i/13).addCard(newdeck.at(i));
newdeck.at(i).setSuit(0);
newdeck.at(i).setCardNum(0);
}
}
//determines if the player still has a card of the same suit in their hand as the leading card played
bool gamePlay::containSuit(spades::card lead, spades::player players){
bool suit = false;
for(int i = 0; i < players.getHand().size(); i++){
if(lead.getSuit() == players.getHand().at(i).get
You have to write default constructor and destructor like this:
struct display
{
display() { }
~display() { }
// ^^^^
};
and not like this:
struct display
{
display();
~display();
};
However, if you do have the latter form in your header and want to keep it that way, you need to add definitions to the source file:
display::display() { }
display::~display() { }
The linker is complaining that it can't find object spades::monitor and function spades::display::drawBox(int, int, int, int). Are these defined anywhere? Please note that when defining a member variable/member function in a .cpp file (or anywhere outside of its class definition), you have to qualify it with the class name. Example:
In .h
namespace spades {
class display {
static display monitor;
void drawBox(int, int, int, int);
};
}
In .cpp
namespace spades {
display display::monitor;
void display::drawBox(int, int, int, int) {
//...
}
}
It's hard to say more without seeing the full code (at least the definitions of the things the linker is complaining about).

How to move relatively to the angle?

I keep looking up on the internet how to move from point A to point B on an angle with a specified distance. When I tried to code it, however, the camera just gets messed up and I'm moving in random directions. I am using SDL/OpenGL with c++ and this is my player function. Right now, I'm trying to get the player to move forwards along the angle.
void player_action()
{
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_MOUSEMOTION:
{
player.rotxl = (screen->w)/2;
player.rotxd = event.motion.x - player.rotxl;
player.rotx = player.rotx + (player.rotxd/4);
if (player.rotx < 0)
{
player.rotx = player.rotx + 360;
};
if (player.rotx > 360)
{
player.rotx = player.rotx - 360;
}
};
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_w:
{player.zvel = 8.0f;}; break;
case SDLK_a:
{player.xvel = 8.0f;}; break;
case SDLK_s:
{player.zvel = -8.0f;}; break;
case SDLK_d:
{player.xvel = -8.0f;}; break;
case SDLK_ESCAPE:
{running = false;}; break;
default: break;
}; break;
case SDL_KEYUP:
switch(event.key.keysym.sym)
{
case SDLK_w:
{player.zvel = 0.0f;}; break;
case SDLK_a:
{player.xvel = 0.0f;}; break;
case SDLK_s:
{player.zvel = 0.0f;}; break;
case SDLK_d:
{player.xvel = 0.0f;}; break;
default: break;
};
break;
};
};
player.x = player.x + float(player.zvel*cos((double)player.rotx));
player.z = player.z + float(player.zvel*sin((double)player.rotx));
glRotatef(player.rotx, 0.0f, 1.0f, 0.0f);
glTranslatef(player.x, player.y, player.z);
SDL_WarpMouse((screen->w/2), (screen->h/2));
};
If I'm correct the math functions sin and cos both take angles in radians, not degrees as player.rotx seems to be. Try the following:
player.x = player.x + float(player.zvel*cos((double)player.rotx*0.0174532925));
player.z = player.z + float(player.zvel*sin((double)player.rotx*0.0174532925));
We're multiplying player.rotx by pi/180, which is how we convert degrees to radians.
Not sure if this is your only problem, but it certainly appears to be one of them.
Two possible sources of the issue I see:
The standard C++ cos/sin function take a function in radians instead of degrees (one radian = 180/pi degrees).
While I'm not familiar with SDL a quick look at the docs makes me think you should be using event.motion.xrel instead of .x. You'll have to change your rotx calculation which should not need the screen width.