【问题标题】:gotoxy in game condign with C游戏中的gotoxy与C一起使用
【发布时间】:2021-08-26 13:01:00
【问题描述】:

我目前正在为大学课程使用 C 语言开发一款 Duck Hunt 风格的游戏,但我遇到了一个问题,即我无法在保持目标移动的同时移动玩家的目标,出于某种原因,我只能分别移动它们。这是代码的精简版本,如果有人可以提供帮助,我将非常高兴。

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include <windows.h>

#define Projetil 207
#define ESC 27

                    //formato da parabola (fazer ainda)
                    //substituir o sleep
                    
int cx = 55;
int cy = 25;

void atirarpcima(){
        int q;
        q = cy+1;
        while (q!=0){
        gotoxy(cx+4, q);
        printf("%c", Projetil);
        Sleep(37);     
        gotoxy(cx+4, q);
        printf("  ");
        q--;
    }
}



void pacman(){
    gotoxy(cx, cy);
    printf(" *     *");
    gotoxy(cx, cy+1);
    printf("***   ***");
    gotoxy(cx, cy+2);
    printf("**** ****");
    gotoxy(cx, cy+3);
    printf(" *******");
    
}

void fantasmasaida(){
    
    srand(time(NULL));
    int y = (rand() % 9)+1;
    int x = (rand() % 7)+14;
    while(x<100){
    gotoxy(x, y);
    printf(" ****** ");
    gotoxy(x, y+1);
    printf("** ** **");
    gotoxy(x, y+2);
    printf("********");
    gotoxy(x, y+3);
    printf("** ** **"); 
    
    gotoxy(x, y);
    printf("        ");
    gotoxy(x, y+1);
    printf("        ");
    gotoxy(x, y+2);
    printf("        ");
    gotoxy(x, y+3);
    printf("        "); 
    x++;
    }
   
    
    
}

void gotoxy(int x, int y)
{
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

int main(){
system("mode con: lines=30 cols=120");
char tecla;
    
pacman();
while (1){
    while(!kbhit()){     
    fantasmasaida();
    }
    tecla = getch();
    if (tecla == 'q'){
    atirarpcima();
    } 
    if (tecla == ESC){
        break;
        }
    
}

return 0;
    }

【问题讨论】:

  • 这是哪个编译器?通常,您会使用多线程将用户输入与两个不同线程中的图形分开。 kbhit() 是旧的单线程 MS DOS 东西。而且用 Sleep() 来停止程序也不是一个好主意。
  • kbhit 也是deprecated,并替换为_kbhit
  • 我正在运行它,它是用 GNU GCC 编译的,文本在屏幕上飞得很快。阻塞函数将阻止同时功能,即使用输入应该在它自己的执行管道中,而用户界面更新(控制台写入)在另一个。
  • 这个大学班是几级?
  • @Lundin, gotoxy() 也是老式的 MS-DOS 风格。并且包括&lt;conio.h&gt; 也...白色和瓶中...(我敢打赌它是Turbo-C 或其他一些Borland 的) 但是在某些时候devcpp 编译器模仿了这个接口。

标签: c imperative-programming


【解决方案1】:

好的,我们开始视频游戏开发 101

执行此操作的正常方法实际上是单线程代码;但是,正如您已经了解到的那样,为动画显示的每个事物(精灵)的循环是错误的。我真的希望他们在入门级的旧书中教过这一点。我一直想知道作为青少年的方式,但无法找到。共有三个基本概念。

首先,我们有主循环。几乎所有内容都进入主循环,如下所示:

struct {
    int old_x;
    int old_y;
    int old_state;
    int x;
    int y;
    int state;
} atirarpcima;

struct {
    int old_x;
    int old_y;
    int old_state;
    int x;
    int y;
    int state;
} pacman;

struct {
    int old_x;
    int old_y;
    int old_state;
    int x;
    int y;
    int state;
} fantasmasaida;

void update_atirarpcima()
{
    atirarpcima.x = atirarpcima.old_x;
    atirarpcima.y = atirarpcima.old_y;
    atirarpcima.x = ... update location
    atirarpcima.y = ... update location
}

void erase_atirarpcima()
{
    gotoxy(atirarpcima.old_x, atirarpcima.old_y);
    printf(" "); //...
}

void draw_atirarpcima()
{
    gotoxy(atirarpcima.old_x, atirarpcima.old_y);
    printf("."); //...
}

void erase_pacman()
{
   // ...
}

void draw_pacman()
{
   // ...
}

void erase_fantasmasaida()
{
   //...
}

void draw_fantasmasaida()
{
   //...
}

void game_loop()
{
    do {
        uint32_t elapsed = GetTickCount();
        // Update game state
        update_atirarpcima();
        update_pacman();
        update_fantasmasaida();
        // Erase the old sprites
        erase_atirarpcima();
        erase_pacman();
        erase_fantasmasaida();
        // Draw the new ones
        draw_atirarpcima();
        draw_pacman();
        draw_fantasmasaida();
        if (_kbhit()) {
            /* do something with keyboard */
        }
        Sleep(16 - (GetTickCount() - Elapsed()); // About 60FPS
    } while (!gameover)
}

现在一切都在同时移动,但游戏不可能,因为目标跳得太多。我们需要解决这个问题。

void update_fantasmasaida()
{
    if (state-- == 0) {
        fantasmasaida.old_x = x;
        fantasmasaida.old_y = y;
        fantasmasaida.x = (rand() % 9)+1;
        fantasmasaida.y = .(rand() % 7)+14;
        state = 60 + rand() % 60;
    }
}

这很简单。

您现在会注意到,射门需要很长时间才能穿过场地。我们会做到这一点,但首先我们需要清理一些重复的垃圾,同时摆脱屏幕上可笑的对象硬编码(以下称为 sprite)。

struct sprite {
    void (*update_sprite)(struct sprite *sprite);
    void (*erase_sprite)(struct sprite *sprite);
    void (*draw_sprite)(struct sprite *sprite);
    int old_x;
    int old_y;
    int x;
    int y;
};

#define GAMESPEED 16
#define MAX_SPRITES 8
struct sprite game_sprites[MAX_SPRITES];

sprite *alloc_sprite()
{
    for (int i = 0; i < MAX_SPRITES; i++)
        if (game_sprites[i].draw_sprite == NULL) {
            memset(game_sprites[i], 0, sizeof(struct sprite));
            return game_sprites[i];
        }
    return NULL;
}

void free_sprite(struct sprite *sprite)
{
    if (!sprite) return;
    sprite->draw_sprite = NULL;
}

void game_init()
{
    struct sprite *pacman = alloc_sprite();
    pacman->x = ...
    pacman->y = ...
    pacman->old_x = pacman->x;
    pacman->old_y = pacman->y;
    pacman->update_sprite = update_pacman;
    pacman->erase_sprite = erase_pacman;
    pacman->draw_sprite = draw_pacman;
    // Same for fantasmasaida
    // but don't do atirarpcima here it's dynamic (and multiple)
}

// All the update/erase/draw routines need to take their slot now:
void update_fantasmasaida(struct sprite *sprite)
{
    if (sprite->state-- == 0) {
        sprite->old_x = x;
        sprite->old_y = y;
        sprite->x = (rand() % 9)+1;
        sprite->y = .(rand() % 7)+14;
        sprite->state = 60 + rand() % 60;
    }
}


void game_loop()
{
    do {
        uint32_t elapsed = GetTickCount();
        for (int i = 0; i < MAX_SPRITES; i++) {
             if (sprites[i].draw_sprite) {
                 sprites[i]->update_sprite(&sprites[i]);
             }
        }
        for (int i = 0; i < MAX_SPRITES; i++) {
             if (sprites[i].draw_sprite) {
                 sprites[i]->erase_sprite(&sprites[i]);
             }
        }
        for (int i = 0; i < MAX_SPRITES; i++) {
             if (sprites[i].draw_sprite) {
                 sprites[i]->draw_sprite(&sprites[i]);
             }
        }
        if (_kbhit()) {
            /* do something with keyboard */
        }
        Sleep(16 - (GetTickCount() - Elapsed()); // About 60FPS
    } while (!gameover)
}

现在是速度问题。最好的解决方法是以小于显示单位的单位跟踪位置。我们将struct 编辑如下:

结构精灵{ void (*update_sprite)(struct sprite *sprite); void (*erase_sprite)(struct sprite *sprite); void (*draw_sprite)(struct sprite *sprite); 诠释旧_x; int old_y; 诠释 sp_x; 诠释 sp_y; 诠释 x; 整数y; };

以某种速度移动它的代码看起来像这样:

     sprite->x += dx;    
     sprite->y += dx;

但现在我们做了以下事情:

int sign(int x)
{
    if (x > 0) return 1;
    if (x < 0) return -1;
    return 0;
}

void apply_deltav(struct sprite *sprite, int dx1000, int dy1000)
{
    sprite->sub_x += dx1000;
    if (abs(sprite->subx > 1000)) {
        sprite->x += sprite->sub_x / 1000;
        sprite->sub_x = sign(sprite->subx) * (abs(sprite->sub_x) % 1000);
    }
    sprite->sub_y += dy1000;
    if (abs(sprite->suby > 1000)) {
        sprite->y += sprite->sub_y / 1000;
        sprite->sub_x = sign(sprite->suby) * (abs(sprite->sub_y) % 1000);
    }
}

我们将apply_deltav 以比显示单位小一千倍的单位给出。 (历史上这是 256,但那是因为 8 位 CPU。)

现在,如果你调整你的常数,它会很顺利。

它变得更深了。如果您继续前进,您会遇到非纯色背景、重叠的 sprite、z-order 等。但这已经足够开始了。

【讨论】:

    猜你喜欢
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多