【问题标题】:Function is being called four times per loop when there are only two calls to it当只有两次调用时,每个循环调用函数四次
【发布时间】:2016-12-07 14:52:02
【问题描述】:

我对 C 编程非常陌生,总体而言,我的编程技能相当生疏。为了学习 C 并重新调整自己的编程方向,我正在挑战自己尝试使用 ncurses 制作简单的 rougelike。

我已经设置了一个“日志”,我应该能够将消息推送到该日志 - 应该显示最近的 10 条消息。为了测试这一点,我已经做到了,每当玩家或非常简单的随机移动的生物迈出一步时,都会推送一条日志消息,上面写着“step [direction]”。然而,尽管他们每个人只采取了一步,但由于某种原因,有四条消息被推送到日志中。倒数第二个始终是角色移动的实际方向,我假设其他两个之一是暴徒移动,但我不知道其他两个的来源。有没有人在我的代码中发现任何可能导致此问题的明显内容?感谢所有帮助,谢谢!

(我认为唯一需要查看的主要相关部分应该是 main() 函数、pushToLog()、printLog() 和 moveCreature()。也就是说,问题可能出在其他地方。我'不确定。)

#include <stdlib.h>
#include <stdio.h>
#include <ncurses.h>
#include <unistd.h>
#include <string.h>

#define up 65
#define down 66
#define right 67
#define left 68
#define quit 113

struct creature {
  int x;
  int y;
  int hp;
  int maxhp;
};

void setupMap();
struct creature setupCreature();
void moveCreature();
void pushToLog();
void printLog();

int breakFlag = FALSE;

char mapShape[15][15];
char mapFeatures[15][15];
char outputLog[10][60];

int main(int argc, char *argv[]){
  struct creature player = setupCreature(4, 4, 100, 100);
  struct creature mob = setupCreature(5, 7, 100, 100);
  setupMap();
  initscr();
  noecho();
  curs_set(FALSE);

  while(1){
    for (int i = 0; i < 15; i++){
      for (int c = 0; c < 15; c++){
        mvprintw(c, i, "%c", mapShape[i][c]);
      }
    }
    mvprintw(player.y, player.x, "%c", '@');
    mvprintw(mob.y, mob.x, "%c", 'd');
    printLog();

    int input = getch();
    moveCreature(input, &player);

    int mobDir = rand() % (68 + 1 - 65) + 65;
    moveCreature(mobDir, &mob);

    refresh();
    usleep(300);

    if (breakFlag == TRUE){
      break;
    }
  }

  endwin();
  return 0;
}


void moveCreature(int dir, struct creature *subject){
  int next;

  if (dir == up){
    next = (subject->y - 1);
    if (mapShape[subject->x][next] != '#'){
      subject->y = next;
      pushToLog("step up     ");
    }
  }
  else if (dir == down){
    next = (subject->y + 1);
    if (mapShape[subject->x][next] != '#'){
     subject->y = next;
     pushToLog("step down     ");
    }
  }
  else if (dir == right){
    next =  (subject->x + 1);
    if (mapShape[next][subject->y] != '#'){
      subject->x = next;
      pushToLog("step right     ");
    }
  }
  else if (dir == left){
    next =  (subject->x - 1);
    if (mapShape[next][subject->y] != '#'){
      subject->x = next;
      pushToLog("step left     ");
    }
  }

  else if (dir == quit){
    breakFlag = TRUE;
  }

}

void pushToLog(char string[]){
  for (int i = 10; i > 0; i--){
    strcpy(outputLog[i], outputLog[i-1]);
  }
  strcpy(outputLog[0], string);
}

void printLog(){
  for (int i = 0; i < 10; i++){
    mvprintw(28-i, 0, outputLog[i]);
  }
}


struct creature setupCreature(int x,int y,int hp,int maxhp){

  struct creature frankenstien;

  frankenstien.x = x;
  frankenstien.y = y;
  frankenstien.hp = hp;
  frankenstien.maxhp = maxhp;

  return frankenstien;
}


void setupMap(){
  for (int i = 0; i < 15; i++){
    for (int c = 0; c < 15; c++){
      mapShape[i][c] = '.';
    }
  }
  for (int i = 0; i < 15; i++){
    mapShape[0][i] = '#';
    mapShape[14][i] = '#';
    mapShape[i][0] = '#';
    mapShape[i][14] = '#';
  }
}

【问题讨论】:

  • 通过调试器运行它。
  • 如果可以,请删除所有不直接相关的代码。见minimal reproducible example。我们不需要看到你的完整游戏。我们所需要的只是一个不能按您期望的方式工作的简短示例程序。这不仅是发布到 SO 的指南;缩小问题范围也是调试代码的好方法。
  • 另外,请发布您期望的输出和您实际得到的输出。
  • 粗略检查告诉我你的 printLog() 是可疑的。它打印队列的全部内容,而不考虑是否已经打印了一行。处理这个问题的一种方法是在 printLog() 的末尾清除队列。 (当然,还有很多其他方法可以解决这个问题。)

标签: c string loops ncurses


【解决方案1】:

您的问题出在输入阶段。您期望通过箭头键发出定向命令,但每次按键都会生成多个字节。除一个以外的所有命令都无效。

作为次要问题,您不会拒绝无效命令。在读取每个命令字符后,您继续移动暴徒,无论该命令是否有效。

总体结果是,当您按下箭头键时,程序会快速完成主循环的三个迭代,一个接一个地,为一个有效的玩家动作生成日志消息,对于两个无效的玩家动作没有日志消息,并记录三个暴徒移动中的每一个的消息。

您可以通过记录无效命令或在调试器中运行程序来检测到这一点。

【讨论】:

  • 因此,实际上,每次按键都会生成几个值,其中有几个被拒绝,其中一个是我的实际按键,我只继续我的实际按键。然而,即使程序循环使用那些被拒绝的值,暴徒仍然移动,导致我们在上面看到的额外移动 - 似乎是有道理的。对于如何拒绝无效命令,您有什么建议或提示吗?
  • @Hstuart,是的,你明白了。至于验证,我会考虑使用与moveCreature() 分开的函数来处理每个命令。当输入有效的移动命令时,它可能会调用 moveCreature(),或者执行适合其他命令的任何操作。它的返回值可以将信息传递回main(),包括暴徒是否可以移动。
  • 你可以得到curses来为你处理键盘转义序列。
  • 还要注意,如果你想在 ncurses 中处理箭头键,那么你可能应该通过类似名称的函数打开 ncurses 的 keypad 选项。这应该使getch() 为每个箭头键按键返回一个值,但这些值将与您现在获得的值不同。有关详细信息,请参阅 getch() 的文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 2011-09-16
  • 2017-07-18
  • 1970-01-01
  • 2020-08-17
  • 2021-02-15
  • 1970-01-01
相关资源
最近更新 更多