スーパーマリオのようなゲームの作成

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Glecion
記事: 6
登録日時: 1年前

スーパーマリオのようなゲームの作成

#1

投稿記事 by Glecion » 1年前

コード:

#include "system.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* データファイルパス */
static char gCharaDataFile[] = "chara.data";

/* 変数初期化 */
GameInfo gGame = {
    GS_Playing,
    { SDL_FALSE, SDL_FALSE, SDL_FALSE, SDL_FALSE },
    30,
    0,
    0,
    0,
    0.0,
    MSG_None,
    { 0, 0, WD_Width *MAP_ChipSize, WD_Height *MAP_ChipSize },
    { 0, 0, WD_Width *MAP_ChipSize, WD_Height *MAP_ChipSize },
    NULL
};
CharaImgInfo gCharaImg[CT_NUM] = {
    { 48, 48, 0, 0, { 0, 0, 0, 0 }, NULL },
    { 24, 24, 0, 0, { 0, 0, 0, 0 }, NULL },
    { 48, 48, 0, 0, { 0, 0, 0, 0 }, NULL },
    { 96, 96, 0, 0, { 0, 0, 0, 0 }, NULL },
};

CharaInfo *gChara;
int gCharaNum;
static const float gGravity = 980; /* 重力加速度 */

/* 関数 */
static SDL_bool IsOutsideWindowLR(CharaInfo *ch, FloatPoint *point, SDL_bool adjust);
static SDL_bool AdjustOverlapBlock(CharaInfo *ch, FloatPoint *point, SDL_Point *adjusted);
static CharaDir GetDirection(CharaInfo *src, CharaInfo *dst);

/* ゲームシステム初期化
 *
 * 返値
 *   正常終了: 0
 *   エラー  : 負数
 */
int InitSystem(void)
{
    int ret = 0;

    /* 乱数初期化 */
    srand(time(NULL));

    /* キャラクター情報初期化 */
    /* ファイルオープン */
    FILE *fp = fopen(gCharaDataFile, "r");
    if (fp == NULL) {
        return PrintError("failed to open data file.");
    }
    /* キャラ総数読込 */
    if (1 != fscanf(fp, "%d", &gCharaNum)) {
        ret = PrintError("failed to read the number of chara data.");
        goto CLOSEFILE;
    }
    /* 領域確保 */
    /* 実演用です.ここのコメントは外して実験に取り組んでください.*/
    gChara = (CharaInfo *)malloc(sizeof(CharaInfo) * gCharaNum);
    if (gChara == NULL) {
        ret = PrintError("failed to allocate memory.");
        goto CLOSEFILE;
    }
    /* キャラ情報読込 */
    for (int i = 0; i < gCharaNum; i++) {
        /* タイプ,HP */
        int type;
        if (2 != fscanf(fp, "%d%d", &type, &(gChara[i].hp))) {
            ret = PrintError("failed to read the chara data 1.");
            break;
        }
        gChara[i].type = type;
        /* キャラの速度(x,y方向) */
        if (2 != fscanf(fp, "%f%f", &(gChara[i].basevel.x), &(gChara[i].basevel.y))) {
            ret = PrintError("failed to read the chara data 2.");
            break;
        }

        /* 初期値の設定 */
        switch (gChara[i].type) {
        case CT_Player:
            gGame.player = i;
            break;
        case CT_Enemy:
            gGame.enemy = i;
            break;
        case CT_Station:
            gGame.restStations++;
            break;
        default:
            break;
        }
    }

CLOSEFILE:
    fclose(fp);

    return ret;
}

/* システム終了処理 */
void DestroySystem(void)
{
    free(gChara);
    return;
}

/* 初期位置の設定
 * 指定タイプのキャラクターに初期位置を設定する
 * 複数ある場合は,非表示で位置設定されていない(0,0)
 * ものを探して設定する
 *
 * 引数
 *   ct: タイプ
 *   r : 初期位置
 */
void SetInitPoint(CharaType ct, SDL_Rect r)
{
    for (int i = 0; i < gCharaNum; i++) {
        if (gChara[i].type == ct && gChara[i].stts == CS_Disable) {
            if (!gChara[i].rect.x && !gChara[i].rect.y) {
                gChara[i].stts    = CS_Enable;
                gChara[i].point.x = gChara[i].rect.x = r.x;
                gChara[i].point.y = gChara[i].rect.y = r.y;
            }
            break;
        }
    }
}

/* 画像のマスクから当たり判定用矩形の算出
 * マスクは一つの矩形であることを前提とする
 * 1ピクセル32ビットを想定
 *
 * 引数
 *   s: マスクのサーフェイス
 *   r: 検出範囲
 *
 * 返値: 算出した矩形
 */
SDL_Rect MeasureMask(SDL_Surface *s, SDL_Rect r)
{
    SDL_Rect ret  = { 0 };
    SDL_Rect ret2 = { 0 };
    /* 検出範囲がマップの高さを超えるときは,
       はみ出た範囲は調べない */
    if (r.y < 0) {
        r.h += r.y;
        r.y = 0;
    }
    /* 検出範囲がマップの幅を超えるときは,
       超えた範囲を(0,y)から調査する
       (マップ左右はつながっているので) */
    if (r.x + r.w > MAP_Width * MAP_ChipSize) {
        SDL_Rect r2 = r;
        r2.x        = 0;
        r2.w        = r.w - (MAP_Width * MAP_ChipSize - r.x);
        ret2        = MeasureMask(s, r2);
        r.w         = MAP_Width * MAP_ChipSize - r.x;
        ret2.x += MAP_Width * MAP_ChipSize;
    }

    /* ピクセルデータの(r.x,r.y)の位置まで移動
       データはOpenCVの画素と同様に保存されている
       (デバイスプログラミング資料p.7参照)
       ただし1ピクセルは32bit */
    Uint32 *p = (Uint32 *)(s->pixels) + s->w * r.y + r.x;

    /* (0,0)から走査して,最初に色のある点を(x,y)とする */
    for (ret.y = 0; ret.y < r.h; ret.y++, p += s->w) {
        for (ret.x = 0; ret.x < r.w; ret.x++) {
            if (p[ret.x] & s->format->Amask) {
                /* (x,y)からx方向に走査して,色のなくなる点までをwとする */
                for (ret.w = 0; ret.x + ret.w < r.w; ret.w++) {
                    if (!(p[ret.x + ret.w] & s->format->Amask))
                        break;
                }
                /* (x,y)からy方向に走査して,色のなくなる点までをhとする */
                for (ret.h = 0; ret.y + ret.h < r.h; ret.h++, p += s->w) {
                    if (!(p[ret.y + ret.h] & s->format->Amask))
                        break;
                }
                if (ret2.w && ret2.h) {
                    ret.w += ret2.w;
                }
                return ret;
            }
        }
    }
    if (ret2.w && ret2.h) {
        ret = ret2;
    }

    return ret;
}

/* x座標のマップ範囲への補正 */
int AdjustXrange(int x)
{
    while (x < 0) {
        x += MAP_Width * MAP_ChipSize;
    }
    return x % (MAP_Width * MAP_ChipSize);
}

/* x座標のマップ範囲への補正float版 */
float AdjustXrangeF(float x)
{
    while (x < 0) {
        x += MAP_Width * MAP_ChipSize;
    }
    while (x >= MAP_Width * MAP_ChipSize) {
        x -= MAP_Width * MAP_ChipSize;
    }
    return x;
}

/* srcから見たdstの方向を返す */
CharaDir GetDirection(CharaInfo *src, CharaInfo *dst)
{
    int diff = src->rect.x - dst->rect.x;
    if ((0 < diff && diff < MAP_Width * MAP_ChipSize / 2)
        || (diff < 0 && -diff > MAP_Width * MAP_ChipSize / 2)) {
        return CD_Left;
    } else {
        return CD_Right;
    }
}

/* 床・壁との重なりを補正する
 *
 * 引数
 *   ch: キャラ
 *   point: 対象座標,補正後座標
 *   adjusted: 補正した値
 *
 * 返値: 補正したときSDL_TRUE
 */
SDL_bool AdjustOverlapBlock(CharaInfo *ch, FloatPoint *point, SDL_Point *adjusted)
{
    SDL_bool ret  = SDL_FALSE;
    SDL_Rect mask = ch->img->mask;
    /* x方向の重なりの補正 */
    /* マスクのマップ上の位置を計算し, */
    mask.x += point->x;
    mask.y += ch->point.y;
    /* その位置の床・壁のマスクを取得 */
    SDL_Rect rr = MeasureMask(gGame.mapMask, mask);
    /* マスクがあるときは重なっていることになるので補正する */
    if (rr.w && rr.h) {
        ret = SDL_TRUE;
        if (rr.x) {
            point->x -= rr.w;
            adjusted->x = -rr.w;
        } else {
            point->x += rr.w;
            adjusted->x = rr.w;
        }
    }
    /* y方向の重なりの補正 */
    mask = ch->img->mask;
    mask.x += point->x;
    mask.y += point->y;
    rr = MeasureMask(gGame.mapMask, mask);
    if (rr.w && rr.h) {
        ret = SDL_TRUE;
        if (rr.y) {
            point->y -= rr.h;
            adjusted->y = -rr.h;
        } else {
            point->y += rr.h;
            adjusted->y = rr.h;
        }
    }
    return ret;
}

/* 画面外に出たか調べる(左右のみ)
 *
 * 引数
 *   ch: キャラ
 *   point: 対象座標,補正後座標
 *   adjust: 補正するか(する場合は画面内に収まるように)
 *
 * 返値: 画面外に出たらSDL_TRUE
 */
SDL_bool IsOutsideWindowLR(CharaInfo *ch, FloatPoint *point, SDL_bool adjust)
{
    SDL_bool ret = SDL_FALSE;
    SDL_Rect r   = ch->img->mask;
    /* 補正しない時は画面から出た時に判断するので,
       画像の幅も考慮する */
    int dw = adjust ? 0 : r.w;
    /* 画像端付近の調整 */
    r.x += point->x;
    if (gGame.rectMap.x + gGame.rectMap.w > MAP_Width * MAP_ChipSize) {
        if (r.x < (gGame.rectMap.x + gGame.rectMap.w - MAP_Width * MAP_ChipSize)) {
            r.x += MAP_Width * MAP_ChipSize;
        }
    }
    /* 左端の調査 */
    if (r.x < (gGame.rectMap.x - dw)) {
        if (adjust)
            point->x += gGame.rectMap.x - r.x;
        ret = SDL_TRUE;
    } else {
        /* 右端の調査 */
        r.x += r.w;
        if (r.x > (gGame.rectMap.x + gGame.rectMap.w + dw)) {
            if (adjust)
                point->x -= r.x - (gGame.rectMap.x + gGame.rectMap.w);
            ret = SDL_TRUE;
        }
    }

    return ret;
}

/* 入力状態から方向の設定 */
void SetInput(void)
{
    gChara[gGame.player].velocity.x = 0.0;
    if (gGame.input.left && !gGame.input.right) {
        gChara[gGame.player].dir        = CD_Left;
        gChara[gGame.player].velocity.x = gChara[gGame.player].basevel.x;
    }
    if (gGame.input.right && !gGame.input.left) {
        gChara[gGame.player].dir        = CD_Right;
        gChara[gGame.player].velocity.x = gChara[gGame.player].basevel.x;
    }
}

/* キャラの状態更新 */
void UpdateCharaStatus(CharaInfo *ch)
{
    switch (ch->stts) {
    case CS_Disable:
        switch (ch->type) {
        case CT_Player:
            gGame.stts = GS_End;
            gGame.msg  = MSG_GameOver;
            break;
        case CT_Enemy:
            if (gGame.stts == GS_Playing) {
                /* 敵を出現させる */
                ch->stts       = CS_Enable;
                ch->point.y    = -(ch->rect.h);
                ch->point.x    = rand() % (MAP_Width * MAP_ChipSize);
                ch->velocity.x = ch->basevel.x;
                ch->velocity.y = 0.0;
                ch->hp         = 1;
            }
            break;
        case CT_Ball:
            if (gGame.input.button1) {
                ch->rect.x = gChara[gGame.player].rect.x;
                ch->rect.y = gChara[gGame.player].rect.y;
                ch->point  = gChara[gGame.player].point;
                ch->dir    = gChara[gGame.player].dir;
                ch->stts   = CS_Enable;
                ch->hp     = 1;
            }
            break;
        default:
            break;
        }
        break;
    case CS_Enable:
        switch (ch->type) {
        case CT_Player:
        case CT_Enemy:
            /* 画面下に落ちたら消す */
            if (ch->point.y >= MAP_Height * MAP_ChipSize) {
                ch->stts = CS_Disable;
            }
            break;
        default:
            break;
        }
        break;
    }
}

/* キャラの移動 */
void MoveChara(CharaInfo *ch)
{
    /* 非表示キャラは計算しない */
    if (ch->stts == CS_Disable) {
        return;
    }
    FloatPoint newpoint = ch->point;
    float newvely       = ch->velocity.y;
    /* x方向の移動(等速運動 x=vt) */
    newpoint.x += ch->dir * ch->velocity.x * gGame.timeStep;

    /* y方向の移動(投げ上げ運動 v=v0-gt, y = v0t - 1/2 gt^2) */
    newvely = ch->velocity.y + gGravity * gGame.timeStep;
    newpoint.y += ch->velocity.y * gGame.timeStep - 1.0 / 2.0 * gGravity * gGame.timeStep * gGame.timeStep;

    /* 床・壁との重なり調整 */
    SDL_Point adjusted = { 0 };
    if (AdjustOverlapBlock(ch, &newpoint, &adjusted)) {
        switch (ch->type) {
        case CT_Enemy:
            if (adjusted.y < 0) {
                /* 床についたときにプレイヤー方向に向く */
                ch->dir = GetDirection(ch, &(gChara[gGame.player]));
                /* ランダムの速度でジャンプ */
                newvely = ch->basevel.y * (rand() % 10);
            }
            break;
        case CT_Player:
            /* 床についたときにジャンプ */
            if (adjusted.y < 0) {
                newvely = (gGame.input.up) ? ch->velocity.y : 0.0;
            }
            break;
        case CT_Ball:
            ch->stts = CS_Disable;
        default:
            break;
        }
    }

    /* 球は画面から外れたら消える */
    if ((ch->type == CT_Ball) && IsOutsideWindowLR(ch, &newpoint, SDL_FALSE)) {
        ch->stts = CS_Disable;
    }

    ch->point      = newpoint;
    ch->velocity.y = newvely;
    ch->rect.x     = newpoint.x;
    ch->rect.y     = newpoint.y;
}

/* 当たり判定 */
void Collision(CharaInfo *ci, CharaInfo *cj)
{
    /* 判定が不要な組み合わせを除外 */
    if (ci->stts == CS_Disable || cj->stts == CS_Disable)
        return;
    if (ci->type == CT_Player && cj->type == CT_Ball)
        return;
    if (cj->type == CT_Player && ci->type == CT_Ball)
        return;
    if (ci->type != CT_Player && ci->type != CT_Ball
        && cj->type != CT_Player && cj->type != CT_Ball)
        return;

    /* マスクをマップ座標に合わせる */
    SDL_Rect mi = ci->img->mask;
    mi.x += ci->rect.x;
    mi.y += ci->rect.y;
    SDL_Rect mj = cj->img->mask;
    mj.x += cj->rect.x;
    mj.y += cj->rect.y;

    /* 当たった */
    if (SDL_HasIntersection(&mi, &mj)) {
        /* HPを減らし,0以下なら消す */
        int n = (ci->hp < cj->hp) ? ci->hp : cj->hp;
        ci->hp -= n;
        if (ci->hp <= 0) {
            ci->stts = CS_Disable;
            if (ci->type == CT_Station) {
                gGame.restStations--;
            }
        }
        cj->hp -= n;
        if (cj->hp <= 0) {
            cj->stts = CS_Disable;
            if (cj->type == CT_Station) {
                gGame.restStations--;
            }
        }
    }
}

/* end of system.c */]

Glecion
記事: 6
登録日時: 1年前

Re: スーパーマリオのようなゲームの作成

#2

投稿記事 by Glecion » 1年前

地面に当たらずに落ちてしまいます、ご教授お願い致します。

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: スーパーマリオのようなゲームの作成

#3

投稿記事 by あたっしゅ » 1年前

東上☆海美☆「
これは、どういうソースァイルなのか ? 解答例 『Windows 生』とか『Windows 用の DxLIb を用いている』。
system.h も公開してください。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

Glecion
記事: 6
登録日時: 1年前

Re: スーパーマリオのようなゲームの作成

#4

投稿記事 by Glecion » 1年前

ここに書き込むのは初めてなので、気分を悪くさせていたらすみません。
SDL2を用いて、スーパーマリオ風のゲームを作ろうとしていて、実行するとマリオが地面をすり抜けて落っこちてしまいます。
これがsystem.hです。

コード:

*
 *  ファイル名	: system.h
 *  機能	: 共通変数,外部関数の定義
 */
#ifndef _SYSTEM_H_
#define _SYSTEM_H_

#include <SDL2/SDL.h>

/* マップサイズ */
enum {
    MAP_Width    = 30,
    MAP_Height   = 10,
    WD_Width     = 15,
    WD_Height    = 10,
    MAP_ChipSize = 48
};

/* マップの種類 */
typedef enum {
    MT_None    = 0,
    MT_Block   = 1, /* 床・壁 */
    MT_Player  = 2, /* プレイヤー */
    MT_Station = 3  /* 拠点 */
} MapType;

/* メッセージ */
typedef enum {
    MSG_None     = 0,
    MSG_GameOver = 1,
    MSG_Clear    = 2,
    MSG_NUM      = 3 /* メッセージの数 */
} Msg;

/* キャラクタータイプ */
typedef enum {
    CT_Player  = 0, /* プレイヤー */
    CT_Ball    = 1, /* 球 */
    CT_Enemy   = 2, /* 敵 */
    CT_Station = 3, /* 拠点 */
    CT_NUM     = 4  /* タイプの数 */
} CharaType;

/* キャラクターの状態 */
typedef enum {
    CS_Disable = 0, /* 非表示 */
    CS_Enable  = 1  /* 表示 */
} CharaStts;

/* キャラクターの向き */
typedef enum {
    CD_Right = 1,
    CD_Left  = -1
} CharaDir;

/* ゲームの状態 */
typedef enum {
    GS_End     = 0, /* 終了 */
    GS_Playing = 1  /* 通常 */
} GameStts;

/* 入力の状態 */
typedef struct {
    SDL_bool up;
    SDL_bool right;
    SDL_bool left;
    SDL_bool button1;
} InputStts;

/* 実数座標 */
typedef struct {
    float x;
    float y;
} FloatPoint;

/* キャラクター画像の情報 */
typedef struct {
    int w;         /* キャラの幅 */
    int h;         /* キャラの高さ */
    int imgW;      /* 画像の幅 */
    int imgH;      /* 画像の高さ */
    SDL_Rect mask; /* 当たり判定の範囲(相対位置) */
    SDL_Texture *image;
} CharaImgInfo;

/* キャラクターの情報 */
typedef struct {
    CharaType type;
    CharaStts stts;
    int hp;              /* HP */
    CharaDir dir;        /* 向き */
    FloatPoint point;    /* 計算上の座標 */
    FloatPoint basevel;  /* 速度の基準値 */
    FloatPoint velocity; /* 速度 */
    SDL_Rect rect;       /* 位置(マップ上の座標) */
    SDL_Rect src;        /* テクスチャの範囲(アニメーション用) */
    CharaImgInfo *img;
} CharaInfo;

/* ゲームの情報 */
typedef struct {
    GameStts stts;
    InputStts input;
    int restTime;     /* 待ち時間 */
    int player;       /* プレイヤーの番号 */
    int enemy;        /* 敵の番号 */
    int restStations; /* 拠点の残り数 */
    float timeStep;   /* 時間の増分 */
    Msg msg;          /* メッセージ */
    SDL_Rect rectMap; /* 画面表示領域 */
    SDL_Rect rectBg;  /* 画面表示領域 */
    SDL_Window *window;
    SDL_Renderer *render;
    SDL_Texture *bg;
    SDL_Texture *map;
    SDL_Texture *msgTexts[MSG_NUM];
    SDL_Surface *mapMask;
} GameInfo;

/* 変数 */
extern GameInfo gGame;
extern CharaImgInfo gCharaImg[CT_NUM];
extern CharaInfo *gChara;
extern int gCharaNum; /* キャラ総数 */

/* 関数 */
/* act.c */
extern int PrintError(const char *str);
/* system.c */
extern int InitSystem(void);
extern void DestroySystem(void);
extern void UpdateCharaStatus(CharaInfo *ch);
extern void MoveChara(CharaInfo *ch);
extern void Collision(CharaInfo *ci, CharaInfo *cj);
extern SDL_Rect MeasureMask(SDL_Surface *s, SDL_Rect r);
extern void SetInput(void);
extern void SetInitPoint(CharaType ct, SDL_Rect r);
extern int AdjustXrange(int x);
extern float AdjustXrangeF(float x);
/* window.c */
extern int InitWindow(void);
extern void DestroyWindow(void);
extern int RenderWindow(void);

#endif
/* end of system.h */

アバター
usao
記事: 1887
登録日時: 11年前

Re: スーパーマリオのようなゲームの作成

#5

投稿記事 by usao » 1年前

自身でデバッグしてください.

「すり抜ける」ときのプログラムの実際の動作を追い,何が起こっているのかを確認してください.

【本来ならば処理がどの経路を通って,変数値がどのように変遷していくのか】というような,そのプログラムの「正しい(しかしながら今現在存在していない=観測できない)振る舞い」の具体的詳細というのは,あなたしか知らない/把握していない のですから,実際にどのように振る舞っているのかを逐一見ていけば,「あなたなら」間違いに気づけるのではありませんか?

アバター
usao
記事: 1887
登録日時: 11年前

Re: スーパーマリオのようなゲームの作成

#6

投稿記事 by usao » 1年前

そのような調査を自身で行ってみて,どうしても解せない現象が観測されるのであれば,
その部分に焦点を絞った具体的な質問を行ってください.

例えば…

実際に変数値を確認してみたら,地面に触れた時にはちゃんとこの判定が働いていて結果としてこの変数の値が〇〇になっている.OKなハズだ.
にもかかわらず,以降の処理の時点での値は××になってしまっているようで,そのために処理が正当な方向に進まないのだ,というところまではわかった.
しかしながら,前述のように変数値が○○から××に変わってしまう理由がわからない.この変数の値を変更するような処理は他には無いハズのに!
何故変わってしまうのでしょうか??

…とかなんとか.

参照魚
記事: 109
登録日時: 6年前

Re: スーパーマリオのようなゲームの作成

#7

投稿記事 by 参照魚 » 1年前

提示されているコードはサブルーチンだけで、全体のフローがわかりません。実際に動かす為には下記のような骨格にサブルーチンを配置したものがあると思います。

コード:

#include "system.h"
int main(){

	InitSystem();
	while ( ***){
		***
	}
	DestroySystem();
	
	return 0;
}

Glecion
記事: 6
登録日時: 1年前

Re: スーパーマリオのようなゲームの作成

#8

投稿記事 by Glecion » 1年前

参照魚 さんが書きました:
1年前
提示されているコードはサブルーチンだけで、全体のフローがわかりません。実際に動かす為には下記のような骨格にサブルーチンを配置したものがあると思います。

コード:

#include "system.h"
int main(){

	InitSystem();
	while ( ***){
		***
	}
	DestroySystem();
	
	return 0;
}
メインルーチンはこれです。分割されています。

コード:

/*
 * ファイル名: pracice.c
 * 機能   : メインルーチン
 */
#include "system.h"
#include <stdio.h>

/* 関数 */
static Uint32 AniTimer(Uint32 interval, void* param);
static int InputEvent(void* data);

/* main */
int main(int argc, char* argv[])
{
    /* 初期化処理 */
    /* SDL */
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
        return PrintError(SDL_GetError());
    }
    /* ゲームシステム */
    if (InitSystem() < 0) {
        PrintError("failed to initialize System");
        goto DESTROYSYSTEM;
    }
    /* ウインドウ */
    if (InitWindow() < 0) {
        PrintError("failed to initialize Windows");
        goto DESTROYALL;
    }
    /* スレッドセーフな整数変数を扱う
     * スレッドの生存確認(0以下でスレッドを終了させる)と
     * 簡易フレームカウンタとして利用 */
    SDL_atomic_t atm;
    SDL_AtomicSet(&atm, 1);
    /* スレッド */
    SDL_Thread* thread = SDL_CreateThread(InputEvent, "InputEvent", &atm);
    if (thread == NULL) {
        PrintError(SDL_GetError());
        goto DESTROYALL;
    }
    SDL_DetachThread(thread);
    /* タイマー */
    SDL_TimerID timer = SDL_AddTimer(100, AniTimer, &atm);
    if (timer == 0) {
        PrintError(SDL_GetError());
        goto RELEASETHREAD;
    }

    /* メインループ */
    while (SDL_AtomicGet(&atm) > 0) {
        /* 終了時は何もしない */
        if (gGame.stts) {
            SetInput();
            for (int i = 0; i < gCharaNum; i++) {
                UpdateCharaStatus(&(gChara[i]));
                MoveChara(&(gChara[i]));
            }
            for (int i = 0; i < gCharaNum; i++) {
                for (int j = i + 1; j < gCharaNum; j++)
                    Collision(&(gChara[i]), &(gChara[j]));
            }
        }
        RenderWindow();

        /* 少し待つ */
        SDL_Delay(10);
        /* フレームカウント */
        SDL_AtomicIncRef(&atm);
    }

    /* 終了処理 */
    SDL_RemoveTimer(timer);
RELEASETHREAD:
    SDL_AtomicSet(&atm, -10);
DESTROYALL:
    DestroyWindow();
DESTROYSYSTEM:
    DestroySystem();
    SDL_Quit();

    return 0;
}

/* エラーメッセージ表示
 *
 * 引数
 *   str: エラーメッセージ
 *
 * 返値: -1
 */
int PrintError(const char* str)
{
    fprintf(stderr, "%s\n", str);
    return -1;
}

/* タイマー処理2(アニメーションの更新) */
Uint32 AniTimer(Uint32 interval, void* param)
{

    /* 時間増分の更新 */
    if (SDL_AtomicGet((SDL_atomic_t*)param) > 0) {
        gGame.timeStep = 0.1 / SDL_AtomicGet((SDL_atomic_t*)param);
        printf("FPS: %d\r", SDL_AtomicGet((SDL_atomic_t*)param) * 10);
        SDL_AtomicSet((SDL_atomic_t*)param, 1);
    }

    /* 転送元範囲の更新(アニメーション) */
    for (int i = 0; i < gCharaNum; i++) {
        /* アニメーションパターンの更新 */
        gChara[i].src.x = (gChara[i].src.x + gChara[i].src.w) % gChara[i].img->imgW;

        /* 状態別画像の更新 */
        gChara[i].src.y = 0;
        switch (gChara[i].type) {
        case CT_Station:
            gChara[i].src.y += (gChara[i].hp > 1) ? 0 : gChara[i].src.h;
            break;
        case CT_Player:
        case CT_Enemy:
            gChara[i].src.x += (gChara[i].dir == CD_Right) ? 0 : gChara[i].src.h;
        default:
            break;
        }
    }
    return interval;
}

/* 入力イベント処理
 *
 * 返値
 *   終了: 0
 */
int InputEvent(void* data)
{
    SDL_Event event;
    while (SDL_AtomicGet((SDL_atomic_t*)data) > 0) {
        /* イベント取得を行わないと,
         * OSが,プロセス停止中と判断し,
         * 画面をグレーにしてしまうので,
         * イベントを使わなくてもイベント取得は行う
         */
        if (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                SDL_AtomicSet((SDL_atomic_t*)data, -10);
                break;
            case SDL_KEYDOWN:
                if (event.key.repeat)
                    break;
                /* キーが押された方向を設定 */
                switch (event.key.keysym.sym) {
                case SDLK_ESCAPE:
                    SDL_AtomicSet((SDL_atomic_t*)data, -10);
                    break;
                case SDLK_UP:
                    gGame.input.up = SDL_TRUE;
                    break;
                case SDLK_RIGHT:
                    gGame.input.right = SDL_TRUE;
                    break;
                case SDLK_LEFT:
                    gGame.input.left = SDL_TRUE;
                    break;
                case SDLK_SPACE:
                    gGame.input.button1 = SDL_TRUE;
                    break;
                default:
                    break;
                }
                break;
            case SDL_KEYUP:
                if (event.key.repeat)
                    break;
                /* 離されたときは解除 */
                switch (event.key.keysym.sym) {
                case SDLK_UP:
                    gGame.input.up = SDL_FALSE;
                    break;
                case SDLK_RIGHT:
                    gGame.input.right = SDL_FALSE;
                    break;
                case SDLK_LEFT:
                    gGame.input.left = SDL_FALSE;
                    break;
                case SDLK_SPACE:
                    gGame.input.button1 = SDL_FALSE;
                    break;
                default:
                    break;
                }
            default:
                break;
            }
        }
    }
    return 0;
}

/* end of pracice.c */

Glecion
記事: 6
登録日時: 1年前

Re: スーパーマリオのようなゲームの作成

#9

投稿記事 by Glecion » 1年前

この問題は解決しました!返信してくれた皆さんありがとうございました。

返信

“C言語何でも質問掲示板” へ戻る