The code block is from mozilla firefox's qcms transform_util.c
void build_output_lut(struct curveType *trc,
uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
{
if (trc->type == PARAMETRIC_CURVE_TYPE) {
float gamma_table[256];
uint16_t i;
uint16_t *output = malloc(sizeof(uint16_t)*256);
if (!output) {
*output_gamma_lut = NULL;
return;
}
compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
*output_gamma_lut_length = 256;
for(i = 0; i < 256; i++) {
output[i] = (uint16_t)(gamma_table[i] * 65535);
}
*output_gamma_lut = output;
} else {
if (trc->count == 0) {
*output_gamma_lut = build_linear_table(4096);
*output_gamma_lut_length = 4096;
} else if (trc->count == 1) {
float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
*output_gamma_lut = build_pow_table(gamma, 4096);
*output_gamma_lut_length = 4096;
} else {
//XXX: the choice of a minimum of 256 here is not backed by any theory,
// measurement or data, however it is what lcms uses.
*output_gamma_lut_length = trc->count;
if (*output_gamma_lut_length < 256)
*output_gamma_lut_length = 256;
*output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
}
}
}
For this loop:
for(i = 0; i < 256; i++) {
output[i] = (uint16_t)(gamma_table[i] * 65535);
}
VC2013 will show:
e:\mozilla\hg\nightly\mozilla-central\gfx\qcms\transform_util.c(490) : info C5002: 由于“500”,循环未向量化
MSDN (http://msdn.microsoft.com/en-us/library/jj658585.aspx) shows:
// Code 500 is emitted if the loop has non-vectorizable flow.
// This can include "if", "break", "continue", the conditional
// operator "?", or function calls.
// It also encompasses correct definition and use of the induction
// variable "i", in that the increment "++i" or "i++" must be the last
// statement in the loop.
But the loop above has no if/break/continue, I don't know why it can't be vectorized.
I suspect that this is due to the fact that variable "i" lies in scope of "if"-statement. Thus it does not belong entirely to "for-loop" scope like it would have been in
for(int i = 0; /* etc. */
At such, the compiler's logic would have been like: "I need not only to make gammatable filles with needed values, but to assign same value to "i" as at loop end". Therefore, no vectorization.
Related
I have a problem with STM32F401CCU. After executing a code disconnecting and connecting power the STM freezes and to unfreeze it I need to reload the build.
I have a complex set of functions that are used to turn an array of int values into a single very long char array. The char array is then written to the long term Flash memory. In order to execute that I do many operations with char arrays and they appear to be executed properly, as saving to Flash is done correctly - I tested Flash content with ST LINK and they are what they should be. But after turning power off/on to actually test if long term Flash memory works I encounter a fatal bug - upon powering STM back on it freezes and the only way to unfreeze it is to reload the build.
After many hours of tests I figured that the problem is with memcpy usage in StringCreateStrForFlashOneProduct. Presumably I created a sort of memory leak, but I don't get what's wrong.
A piece of example operation that causes STM to freeze:
static char send_save_string_final[2048];
char sssssend_char[2048] = "000000NewProduct";
char *StringCreateStrForFlashOneProduct(const int someint)
{
memcpy(sssssend_char, "000000008888008000000000888800800000000088880080", 48);
memcpy(send_save_string_final, sssssend_char, 48);
return send_save_string_final;
}
The code I actually use:
FUNCTION ONE - Creates a single array of chars and then saves it to flash memory
static char output_all_sorts[6001] = "";
char all_save_string[6001] = "";
char sssssend_char_all_sorts[2048] = "00000AllProducts";
char append_char_numz_all_sorts[17] = "0000000000000000";
void FlashSaveAllSorts(void)
{
strcpy(sssssend_char_all_sorts, "00000AllProducts");
sssssend_char_all_sorts[2047] = 0;
strcpy(output_all_sorts, "0");
output_all_sorts[6000] = 0;
strcpy(all_save_string, "00000AllProducts");
all_save_string[6000] = 0;
strcpy(append_char_numz_all_sorts, "0000000000000000");
append_char_numz_all_sorts[16] = 0;
for (int doin_int = 0; doin_int <= 6; doin_int++)
{
strcpy(sssssend_char_all_sorts, StringCreateStrForFlashOneProduct(doin_int));
strcat(all_save_string, sssssend_char_all_sorts);
strcpy(output_all_sorts, all_save_string);
}
strcpy(output_all_sorts, all_save_string);
output_all_sorts[strlen(output_all_sorts)] = 0;
Flash_Write_Data(0x08020000, output_all_sorts, 3000);
}
FUNCTION TWO - Creates a single line with all data for one product
static char send_save_string_final[2048] = "0";
char sssssend_char[2048] = "000000NewProduct";
char append_char_numz[17] = "0000000000000000";
char *StringCreateStrForFlashOneProduct(const int someint)
{
strcpy(send_save_string_final, "0");
send_save_string_final[2047] = 0;
strcpy(sssssend_char, "000000NewProduct");
sssssend_char[2047] = 0;
strcpy(append_char_numz, "0000000000000000");
append_char_numz[16] = 0;
strncpy(append_char_numz, StringCreateStringFromIntTwo(someint), 17);
append_char_numz[16] = 0;
strcat(sssssend_char, append_char_numz);
for (int kk = 0; kk < 3; kk = kk + 1)
{
char append_char_one[17] = "0000000000000000";
for (int jj = 0; jj < 12; jj = jj + 1)
{
char append_char[17] = "0000000000000000";
memcpy(append_char, StringCreateStringFromIntTwo(tunable_vars_machine_for_flash[someint][kk][jj]), 17);
strcat(sssssend_char, append_char);
}
memcpy(append_char_one, "000000000MenuEnd", 17);
strcat(sssssend_char, append_char_one);
}
char append_char_end_zeros[17] = "0000000000000000";
memcpy(append_char_end_zeros, StringCreateStringFromIntTwo(0), 17);
strcat(sssssend_char, append_char_numz);
memcpy(send_save_string_final, sssssend_char, 2047);
return send_save_string_final;
}
FUNCTION THREE - Creates a 16-chars array with a following format 000000000000001333, zeros are required to have consistent length of saved string, 1333 is example data value:
char *StringCreateStringFromIntTwo(int base_int_base)
{
//clearprint("StringCreateStri");
int base_int = base_int_base;
if (base_int == 0)
{
base_int = 999999; /// avoid saving zero to flash memory
}
static char send_char_final[17] = "0000000000000000";
char send_char[16] = "00000000";
static char send_char_sixteen_zeros[17] = "0000000000000000";
int legnewpigwphjh = strlen(send_char);
char str_zero[2] = "0";
char str_two_zeros[3] = "00";
char str_three_zeros[4] = "000";
char str_four_zeros[5] = "0000";
char str_five_zeros[6] = "00000";
char str_six_zeros[7] = "000000";
char str_seven_zeros[8] = "0000000";
char str_eight_zeros[9] = "00000000";
char str_sixteen_zeros[17] = "0000000000000000";
int int_mem_length = countDigits(base_int);
char str_mem_write_int[9];
sprintf(str_mem_write_int, "%d", base_int);
if (int_mem_length == 7)
{
strcat(send_char, str_zero);
}
if (int_mem_length == 6)
{
strcat(send_char, str_two_zeros);
}
if (int_mem_length == 5)
{
strcat(send_char, str_three_zeros);
}
if (int_mem_length == 4)
{
strcat(send_char, str_four_zeros);
}
if (int_mem_length == 3)
{
strcat(send_char, str_five_zeros);
}
if (int_mem_length == 2)
{
strcat(send_char, str_six_zeros);
}
if (int_mem_length == 1)
{
strcat(send_char, str_seven_zeros);
}
strcat(send_char, str_mem_write_int);
strcpy(send_char_final, str_sixteen_zeros);
strcpy(send_char_final, send_char);
return send_char_final;
}
Your code is way too complicated. There are too many string copies and concatenations, and countless useless operations. You do not seem to master pointers, nor basic memory layout concepts. Above all, given your skill level, you should absolutely avoid strncpy and memcpy.
Here is a simpler approach with functions that construct the appropriate string format at the end of the buffer and return the number of characters written. With this method, composing a long string is much simpler and much less error prone:
// function to construct a fixed length string of digits with leading 0s
// buffer must point to an array at least 17 bytes long
// returns the number of characters, ie: 16
int FormatNumber16(char *buffer, int someint) {
if (someint == 0) {
// This test seems bogus: the product number 0 will be written as 999999
// and so will the MenuEnd number
someint = 999999; // avoid writing 0 ???
}
return snprintf(buffer, 17, "%016d", someint);
}
// function to construct the product string to store to flash
// buffer must point to an array at least 641 bytes long
// returns the number of characters, ie: 640
int FormatOneProduct(char *buffer, int product_nb) {
int pos = 0;
pos += snprintf(buffer + pos, 17, "%016s", "NewProduct");
pos += FormatNumber16(buffer + pos, product_nb);
for (int kk = 0; kk < 3; kk++) {
for (int jj = 0; jj < 12; jj++) {
pos += FormatNumber16(buffer + pos,
tunable_vars_machine_for_flash[product_nb][kk][jj]);
}
}
pos += snprintf(buffer + pos, 17, "%016s", "MenuEnd");
// vvv this will actually append 0000000000999999 ?
pos += FormatNumber16(buffer + pos, 0);
// pos should be 640 = 16 + 16 + 12*3*16 + 16 + 16
return pos;
}
// function to write all product data to flash (7 products)
void FlashSaveAllSorts(void) {
char buffer[6001] = ""; // no need for global data
int pos = 0;
pos += snprintf(buffer + pos, 17, "%016s", "AllProducts");
// append the description of all 7 products (0 to 6 included)
for (int product_nb = 0; product_nb <= 6; product_nb++) {
pos += FormatOneProduct(buffer + pos, product_nb);
}
// buffer contains 4496 = 16 + 640*7 characters, why write 3000 bytes?
Flash_Write_Data(0x08020000, buffer, 3000);
}
Some example functions:
char *StringCreateStrForFlashOneProduct(char *buff, size_t len, int someint)
{
int mask = 1000000000;
char *wrk = buff;
if(!someint) someint = 999999;
memset(buff, '0', len - 10);
wrk += len - 10;
while(mask)
{
*wrk++ = '0' + abs(someint / mask);
someint %= mask;
mask /= 10;
}
*wrk = 0;
return buff;
}
example usage:
int main(void)
{
char str[17];
printf("`%s`\n", StringCreateStrForFlashOneProduct(str, 16, 1234));
}
We have a project that controls a led strip of 80, that is connected via mobile app. The components we used are as follows:
Arduino Nano Board
HM - 10 Bluetooth module
WS2812b Led Strip
On the app, user selects the colors(up to 5), animation (optional), animation speed and brightness. The selected configuration is transmitted to the BLE module with a certain throttle and debouncing. The color(selected on a color wheel on the app) and brightness transmits fine, without any issues.
The problem we encounter, is that when certain animation is active, if the user changes the animation speed via the app, the arduino part locks itself and will not accept any more commands.
To activate a configuration with animation, we send the data from app to arduino as follows:
<l255180200,240135068:089;04200>
The format is: < [mode] [colors(with ',')] : [brightness] ; [animationCode(2 digit)] [animationSpeed] >
At first, we had some inconsistencies with consecutive data, so we implemented the following for data acquisition:
void loop()
{
// Read all serial data available, as fast as possible
while (bleSerial.available() > 0)
{
char inChar = bleSerial.read();
if (inChar == SOP)
{
index = 0;
inData[index] = '\0';
started = true;
ended = false;
}
else if (inChar == EOP)
{
ended = true;
break;
}
else
{
if (index < 79)
{
inData[index] = inChar;
index++;
inData[index] = '\0';
}
}
}
// We are here either because all pending serial
// data has been read OR because an end of
// packet marker arrived. Which is it?
if (started && ended)
{
// The end of packet marker arrived. Process the packet
Serial.println(inData);
Serial.println(inData[0]);
char mode = inData[0];
if (mode == 'p')
{
togglePower(inData);
finalizeRead();
return;
}
if (mode == 'b')
{
changeBrightness(inData);
finalizeRead();
return;
}
if (mode == 't')
{
char *themeNo = strtok(NULL, ";");
int themeCode = valueFromString(themeNo, 0, 2);
theme(themeCode);
}
// if (mode == 'e') {
// sound();
// return;
// }
char *colorsWithBrightness = strtok(inData, ";");
char *animation = strtok(NULL, ";");
char *colors = strtok(colorsWithBrightness, ":");
char *brightness = strtok(NULL, ":");
custom(colors, brightness, animation);
finalizeRead();
}
}
void finalizeRead()
{
// Reset for the next packet
started = false;
ended = false;
index = 0;
inData[index] = '\0';
}
int valueFromString(char *string, int start, int width)
{
int value = 0;
for (int n = 0; n < width; n++)
value = value * 10 + string[start + n] - '0';
return value;
}
So the custom function is called when the mode == 'l'. And in custom function, we perform as follows:
void custom(char *colors, char *brightness, char *animation)
{
char *trueColors = strtok(colors, "l");
char *colorsSplit = strtok(trueColors, ",");
int colorCount = 0;
uint32_t colorArray[5] = {};
while (colorsSplit != NULL)
{
int r = valueFromString(colorsSplit, 0, 3);
int g = valueFromString(colorsSplit, 3, 3);
int b = valueFromString(colorsSplit, 6, 3);
colorArray[colorCount] = strip.Color(r, g, b);
colorCount++;
colorsSplit = strtok(NULL, ",");
}
int ledsPerSegment = ledCount / colorCount;
for (int n = 0; n < colorCount; n++)
{
int currentSegment = (n + 1) * ledsPerSegment;
Serial.println(currentSegment);
for (int z = n * ledsPerSegment; z < (n + 1) * ledsPerSegment; z++)
{
strip.setPixelColor(z, colorArray[n]);
}
}
strip.setBrightness(atoi(brightness));
strip.show();
Serial.println("Done");
if (animation)
{
int animationCode = valueFromString(animation, 0, 2);
int animationSpeed = valueFromString(animation, 2, 3);
Serial.println(animationSpeed);
if (animationCode == 1)
breath(animationSpeed, atoi(brightness));
else if (animationCode == 7)
pulse(animationSpeed, colorArray, colorCount);
}
}
The problem occurs in pulse animation, which is like this:
void pulse(int wait, uint32_t colorArray[], int colorCount)
{
black();
while (bleSerial.available() <= 0)
{
for (i = 0; i < colorCount && bleSerial.available() <= 0; i++)
{
for (j = 0; j < ledCount / 2 && bleSerial.available() <= 0; j++)
{
strip.setPixelColor(39 - j, colorArray[i]);
strip.setPixelColor(40 + j, colorArray[i]);
strip.show();
delay(wait);
}
black();
}
}
}
We initialize the animation without any issues using the following:
<l255180200,240135068:089;04200>
As soon as we adjust the animation speed from the app, we send about 2 new configurations per second, which is the same as above, only with different speed (last 3 characters before '>'). The arduino part randomly receives the data incorrectly, losing 2-3 characters, which is like <l2551800,240135068:089;04200>
The loss of characters usually occurs on different parts of the string, but always consecutive characters are misread happen each time. Also, sometimes we get a backwards question mark.
We are not sure where we implement wrong, so any help is appreciated on solving this issue.
Thanks!
I am trying to enter an if statement when my counter reaches the value 51, and then increment the counter inside the if statement so that it is never true again:
void imu_raw_handler (const lcm_recv_buf_t *rbuf, const char *channel,
const imu_raw_t *msg, void *userdata) {
for(counter = 1; counter < 51; counter++)
{
gyro_roll = (gyro_roll + gyro_roll_old*(counter-1))/counter;
gyro_pitch = (gyro_pitch + gyro_pitch_old*(counter-1))/counter;
gyro_yaw = (gyro_yaw + gyro_yaw_old*(counter-1))/counter;
accel_x = (accel_x + accel_x_old*(counter-1))/counter;
accel_y = (accel_y + accel_y_old*(counter-1))/counter;
accel_z = (accel_z + accel_z_old*(counter-1))/counter;
}
if (counter == 51){
// set zero points
axo = accel_x;
ayo = accel_y;
azo = accel_z;
gro = gyro_roll;
gpo = gyro_pitch;
gyo = gyro_yaw;
printf("Ready for Takeoff\n");
counter++;
}
}
Edit: My main function looks like this. It is supposed to subscribe to a streaming topic for as long as it is running. I define a counter as a global variable.
int main()
{
lcm_t *lcm = lcm_create(NULL);
imu_raw_t_subscribe(lcm, "IMU_RAW", imu_raw_handler, NULL);
// Enter read loop
while (1) {
if(!lcm_handle_timeout(lcm, 1000)) {
printf("Failed read from IMU_RAW channel\n");
}
}
lcm_destroy(lcm);
}
But this will repeatedly print my message forever rather than making counter = 52 and never happening again. Why does this not work?
The problem most likely exists outside these code snippets. Please check if you have no prints somewhere else.
The problem is because you call that function in a while, and because of
for(counter = 1; counter < 51; counter++)
the counter will become again 51 (everytime the function is called) and then will enter again in that if (counter == 51).
To resolve that you could declare a global variable int ok = 0; and use it like this:
void imu_raw_handler (const lcm_recv_buf_t *rbuf, const char *channel,
const imu_raw_t *msg, void *userdata) {
for(counter = 1; counter < 51; counter++)
{
gyro_roll = (gyro_roll + gyro_roll_old*(counter-1))/counter;
gyro_pitch = (gyro_pitch + gyro_pitch_old*(counter-1))/counter;
gyro_yaw = (gyro_yaw + gyro_yaw_old*(counter-1))/counter;
accel_x = (accel_x + accel_x_old*(counter-1))/counter;
accel_y = (accel_y + accel_y_old*(counter-1))/counter;
accel_z = (accel_z + accel_z_old*(counter-1))/counter;
}
if (ok == 0){ //will enter here once, only if you don't modify the ok anytime
// set zero points
axo = accel_x;
ayo = accel_y;
azo = accel_z;
gro = gyro_roll;
gpo = gyro_pitch;
gyo = gyro_yaw;
printf("Ready for Takeoff\n");
ok = 1; //this will be done once
}
}
I hope this is correct.
The for loop right before the if uses the counter variable, so every time the loop is finished, counter will be 51.
My guess is that the loop shouldn't be there at all, it doesn't seem to make any sense.
I have been playing around with programming for arduino but today i've come across a problem that i can't solve with my very limited C knowledge.
Here's how it goes.
I'm creating a pc application that sends serial input to the arduino (deviceID, command, commandparameters). This arduino will transmit that command over RF to other arduino's. depending on the deviceID the correct arduino will perform the command.
To be able to determine the deviceID i want to split that string on the ",".
this is my problem, i know how to do this easily in java (even by not using the standard split function), however in C it's a totally different story.
Can any of you guys tell me how to get this working?
thanks
/*
Serial Event example
When new serial data arrives, this sketch adds it to a String.
When a newline is received, the loop prints the string and
clears it.
A good test for this is to try it with a GPS receiver
that sends out NMEA 0183 sentences.
Created 9 May 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/SerialEvent
*/
String inputString; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
String[] receivedData;
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
// print the string when a newline arrives:
if (stringComplete) {
Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n') {
stringComplete = true;
}
// add it to the inputString:
if(stringComplete == false) {
inputString += inChar;
}
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
}
}
String[] splitCommand(String text, char splitChar) {
int splitCount = countSplitCharacters(text, splitChar);
String returnValue[splitCount];
int index = -1;
int index2;
for(int i = 0; i < splitCount - 1; i++) {
index = text.indexOf(splitChar, index + 1);
index2 = text.indexOf(splitChar, index + 1);
if(index2 < 0) index2 = text.length() - 1;
returnValue[i] = text.substring(index, index2);
}
return returnValue;
}
int countSplitCharacters(String text, char splitChar) {
int returnValue = 0;
int index = -1;
while (index > -1) {
index = text.indexOf(splitChar, index + 1);
if(index > -1) returnValue+=1;
}
return returnValue;
}
I have decided I'm going to use the strtok function.
I'm running into another problem now. The error happened is
SerialEvent.cpp: In function 'void splitCommand(String, char)':
SerialEvent:68: error: cannot convert 'String' to 'char*' for argument '1' to 'char* strtok(char*, const char*)'
SerialEvent:68: error: 'null' was not declared in this scope
Code is like,
String inputString; // a string to hold incoming data
void splitCommand(String text, char splitChar) {
String temp;
int index = -1;
int index2;
for(temp = strtok(text, splitChar); temp; temp = strtok(null, splitChar)) {
Serial.println(temp);
}
for(int i = 0; i < 3; i++) {
Serial.println(command[i]);
}
}
This is an old question, but i have created some piece of code that may help:
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
This function returns a single string separated by a predefined character at a given index. For example:
String split = "hi this is a split test";
String word3 = getValue(split, ' ', 2);
Serial.println(word3);
Should print 'is'. You also can try with index 0 returning 'hi' or safely trying index 5 returning 'test'.
Hope this help!
Implementation:
int sa[4], r=0, t=0;
String oneLine = "123;456;789;999;";
for (int i=0; i < oneLine.length(); i++)
{
if(oneLine.charAt(i) == ';')
{
sa[t] = oneLine.substring(r, i).toInt();
r=(i+1);
t++;
}
}
Result:
// sa[0] = 123
// sa[1] = 456
// sa[2] = 789
// sa[3] = 999
For dynamic allocation of memory, you will need to use malloc, ie:
String returnvalue[splitcount];
for(int i=0; i< splitcount; i++)
{
String returnvalue[i] = malloc(maxsizeofstring * sizeof(char));
}
You will also need the maximum string length.
The C way to split a string based on a delimiter is to use strtok (or strtok_r).
See also this question.
I think your idea is a good start point. Here is a code that i use (to parse HTTP GET REST requests with an Ethernet shield).
The idea is to use a while loop and lastIndexOf of and store the strings into an array (but your could do something else).
"request" is the string you want to parse (for me it was called request because.. it was).
int goOn = 1;
int count = -1;
int pos1;
int pos2 = request.length();
while( goOn == 1 ) {
pos1 = request.lastIndexOf("/", pos2);
pos2 = request.lastIndexOf("/", pos1 - 1);
if( pos2 <= 0 ) goOn = 0;
String tmp = request.substring(pos2 + 1, pos1);
count++;
params[count] = tmp;
// Serial.println( params[count] );
if( goOn != 1) break;
}
// At the end you can know how many items the array will have: count + 1 !
I have used this code successfully, but i thing their is an encoding problem when i try to print params[x]... i'm alos a beginner so i don't master chars vs string...
Hope it helps.
I believe this is the most straight forward and quickest way:
String strings[10]; // Max amount of strings anticipated
void setup() {
Serial.begin(9600);
int count = split("L,-1,0,1023,0", ',');
for (int j = 0; j < count; ++j)
{
if (strings[j].length() > 0)
Serial.println(strings[j]);
}
}
void loop() {
delay(1000);
}
// string: string to parse
// c: delimiter
// returns number of items parsed
int split(String string, char c)
{
String data = "";
int bufferIndex = 0;
for (int i = 0; i < string.length(); ++i)
{
char c = string[i];
if (c != ',')
{
data += c;
}
else
{
data += '\0';
strings[bufferIndex++] = data;
data = "";
}
}
return bufferIndex;
}
I'm hacking away at a simple game to teach myself C and I've come up against an infuriatingly simple problem that I haven't been able to Google an answer to.
Code follows, apologies for its noobie terribleness (criticisms appreciated!):
#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>
#define AMOUNT_OF_ENEMIES 10
#define AMOUNT_OF_PIXELS_TO_MOVE 50.0
struct enemy
{
int alive;
SDL_Rect rect;
};
void create_enemy(struct enemy *position)
{
// Take a pointer to an array. Iterate through array looking for any 'dead' instances.
// (Re)initialise when found, ignore entirely if array is full of alive instances.
int j = 0;
while(position[j].alive == 1 && j < AMOUNT_OF_ENEMIES)
{
++j;
}
if(position[j].alive == 0)
{
position[j].alive = 1;
position[j].rect.y = 0;
}
}
void update_enemies(struct enemy *position)
{
// Iterate through a passed array looking for alive instances. If found increment vertical position,
// unless instance is at bottom of screen in which case it's marked as dead.
int j = 0;
while(j < AMOUNT_OF_ENEMIES)
{
if(position[j].alive == 1)
{
position[j].rect.y += 1;
if(position[j].rect.y > 570)
{
position[j].alive = 0;
}
}
++j;
}
}
int main(void)
{
// INITS *********************************************************************
int k;
int current_time = 0;
int previous_time = 0;
float difference_in_time = 0.0;
// Load SDL library
if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Problem, yo\n");
return 1;
}
// Setup event queue
SDL_Event event;
// Create array to store enemys, initialise it
struct enemy *enemy_array = malloc(sizeof(struct enemy) * AMOUNT_OF_ENEMIES);
int j;
for(j = 0; j < AMOUNT_OF_ENEMIES; ++j)
{
enemy_array[j].alive = 0;
enemy_array[j].rect.x = 150;
enemy_array[j].rect.y = 0;
}
// Create an array to flag keypresses, initialise it
int pressed_keys[323];
int l;
for(l = 0; l < 323; ++l)
{
pressed_keys[l] = 0;
}
// Create surfaces
SDL_Surface *screen = SDL_SetVideoMode(300, 600, 0, SDL_HWSURFACE);
int black = SDL_MapRGB(screen->format, 0, 0, 0);
SDL_Surface *tower = SDL_LoadBMP("tower.bmp");
SDL_Rect tower_rect;
tower_rect.x = 50;
tower_rect.y = 0;
tower_rect.w = 200;
tower_rect.h = 600;
SDL_Surface *dude = SDL_LoadBMP("dude.bmp");
float dude_x = 0.0;
SDL_Rect dude_rect;
dude_rect.x = 120;
dude_rect.y = 500;
dude_rect.w = 60;
dude_rect.h = 100;
SDL_Surface *enemy = SDL_LoadBMP("enemy.bmp");
// GAME LOOP *****************************************************************
while(1)
{
current_time = SDL_GetTicks();
difference_in_time = (float)(current_time - previous_time) / 1000;
previous_time = current_time;
if(SDL_PollEvent(&event))
{
if(event.key.keysym.sym == SDLK_DOWN)
{
create_enemy(enemy_array);
}
else
{
switch(event.type)
{
case SDL_QUIT:
printf("NOOOOOO\n");
SDL_FreeSurface(screen);
SDL_FreeSurface(tower);
SDL_FreeSurface(enemy);
free(enemy_array);
SDL_Quit();
return 0;
case SDL_KEYDOWN:
pressed_keys[event.key.keysym.sym] = 1;
break;
case SDL_KEYUP:
pressed_keys[event.key.keysym.sym] = 0;
break;
}
}
}
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
update_enemies(enemy_array);
SDL_FillRect(screen, NULL, black);
SDL_BlitSurface(tower, NULL, screen, &tower_rect);
for(k = 0; k < AMOUNT_OF_ENEMIES; ++k)
{
if(enemy_array[k].alive == 1)
{
SDL_BlitSurface(enemy, NULL, screen, &enemy_array[k].rect);
}
}
SDL_BlitSurface(dude, NULL, screen, &dude_rect);
SDL_Flip(screen);
}
return 0;
}
The issue arises at this part:
if(pressed_keys[SDLK_LEFT] && dude_rect.x > 50)
{
dude_rect.x -= (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
if(pressed_keys[SDLK_RIGHT] && dude_rect.x < 190)
{
dude_rect.x += (AMOUNT_OF_PIXELS_TO_MOVE * difference_in_time);
}
The 'dude' object moves to the left correctly, but nothing happens when the right arrow key is pressed.
Adding a printf tells me the if statement is being executed correctly. Removing difference_in_time makes it work, so it's either something to do with that variable or the operation of it and AMOUNT_OF_PIXELS_TO_MOVE.
I just can't for the life of me figure out why the former block executes correctly and the latter (which is essentially the same thing) doesn't. I'm sure it's something simple I've overlooked but I'm going insane trying to find it.
Your problem is due to rounding.
For your "dude" you are using a SDL_Rect, that uses integer coordinates (short int if I remember correct).
You configured your dude speed to 50 and if your game is running at 60fps (probably due to its simplicity and it may be much more if vsync is off) you will get each frame a movement value of 0.83333.
This value will be truncated to a int and the result will be zero, for example, if dude.x is 10 and you press right, the calculated value will be 10.83 and when truncated this will result in 10.
For left, it works because the value is rounded down, assuming again dude.x is 10, when left is pressed, on the first iteration the calculated value would be 9.17, truncating this will give you 9.
Simple, bad and Hack Solution
Increase AMOUNT_OF_PIXELS_TO_MOVE to a higher value that forces the int to increase, this will fix the problem.
Good Solution
Does not use SDL_Rect for storing your characters position, create a "MyRect" and use float values in it and only does rounding when drawing the character. Actually you only need to store the character position, so I would create a Point2D struct with only x and y and use this to keep track of characters position.