弾の描画について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
エミリア

弾の描画について

#1

投稿記事 by エミリア » 5年前

[1] 質問文
 [1.1] 自分が今行いたい事は何か
   アクションゲーム制作
 [1.2] どのように取り組んだか(プログラムコードがある場合記載)
↓main.cpp
   

コード:

#include "ALL.h"


int playerSpeed = 5; // 歩く速さ
int playerX = 0; // X座標
int playerY = 256; // Y座標
int playerImgIndex = 0; // 描画する画像の番号
int count = 0; // カウント
int timer;
int WaitAnimTimer = 0;
void Player();
int WINAPI WinMain(HINSTANCE h1, HINSTANCE hp, LPSTR lpC, int nC)
{
    // ウィンドウモードにする
    ChangeWindowMode(TRUE);
    SetGraphMode(1280, 720, 32);
    // DXライブラリの初期化
    if (DxLib_Init() == -1)	return -1;

    // 裏画面を描画対象にする
    SetDrawScreen(DX_SCREEN_BACK);

    // 正常にDXライブラリが処理されていて、ESCPEキーが押されているときのみループを脱出する
    while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        // 画面消去
        ClearDrawScreen();

        // この中に行いたい処理を書いていく
        static bool Initflag = true;
        if (Initflag) {
            load();
            PlayerInit();
            Initflag = false;
        };

        PlayerUpdate();
        PL_Shot_Update();

        timer++;
        WaitAnimTimer++;
        // 画面入れ替え
        ScreenFlip();
    }

    // DXライブラリの終了処理
    DxLib_End();

    return 0;
}

↓Player.cpp

コード:

#include "ALL.h"

#define JPOWER 7;

//ジャンプ用変数
bool jflag = false;
bool onGround = true;
static int jcnt;
int gravity = 1;


OBJ2D Player;
void PlayerDraw(OBJ2D*p);
void PlayerMain(OBJ2D*p);
void PlayerSet(OBJ2D*p, int x, int y, int type);
void PlayerAnim();
void Playerwait();
bool Key_prev(const int Key, const int num);
struct PLAYER_SET_DATA 
{
    int x, y;
    int mvAlg;
};

PLAYER_SET_DATA p_st_data[] = {
    {0,592,0}
};
void PlayerInit()
{
    //プレイヤーの初期設定
    for (int i = 0; i < 1; i++) {
        OBJ2D*p = &Player;
        if (p->exist) continue;

        PlayerSet(p, p_st_data[i].x, p_st_data[i].y, p_st_data[i].mvAlg);
    };
};

void PlayerUpdate()
{
    for (int i = 0; i < 1; i++) {
        OBJ2D*p = &Player;

        if (!p->exist)continue;

        switch (p->mvAlg)
        {
        case 0:PlayerMain(p); break;
        
        }

        PlayerDraw(p);
    };
};

void PlayerSet(OBJ2D*p, int x, int y, int type)
{
   p->pos.x = x;
   p->pos.y = y;
   p->mvAlg = type;
   p->exist = true;
};

void PlayerMain(OBJ2D*p)
{
    static int MouseX, MouseY;
    GetMousePoint(&MouseX, &MouseY);
    SetMousePoint(MouseX, MouseY);
    int mouse = GetMouseInput();
    unsigned int Color;
    Color = GetColor(255, 255, 255);
    DrawFormatString(0, 0, Color, "Player.pos.y:%d", Player.pos.y);
    DrawFormatString(0, 15, Color, "Player.speed.y:%d", p->speed.y);
    DrawFormatString(0, 30, Color, "Player.speed.x:%d", Player.speed.x);
    DrawFormatString(0, 45, Color, "Player.pos.x:%d", Player.pos.x);
    DrawFormatString(0, 60, Color, "jcnt:%d", jcnt);
    DrawFormatString(0, 75, Color, "Count:%d", count);
    DrawFormatString(0, 90, Color, "playerImgIndex:%d", playerImgIndex);
    DrawFormatString(0, 105, Color, "onGround:%d", onGround);
    DrawFormatString(0,120, Color, "p->dir:%d", p->dir);

    p->speed.x = 0;
    gravity = 1;
    //プレイヤーの向きを設定
    //右向き
    if (p->pos.x > MouseX&&onGround) {
        p->dir = 1;
    }
    //左向き
    if (p->pos.x < MouseX&&onGround) {
        p->dir = -1;
    }

    //キーボード入力
    //右移動
    if (CheckHitKey(KEY_INPUT_A))
    {
        p->speed.x = -5;
    };
    //左移動
    if (CheckHitKey(KEY_INPUT_D))
    {
        p->speed.x = 5;
    };

    if (timer == 4) {
        PlayerAnim();
        timer = 0;
    };
    if (WaitAnimTimer == 8) {
        Playerwait();
        WaitAnimTimer = 0;
    };
    //ジャンプ処理
    if (jflag == true) {
        jcnt++;
        int cntmax = 0;
        Player.speed.y -= jcnt;
        cntmax = JPOWER;
        if (p->dir == 1)playerImgIndex = 26;
        else if(p->dir==-1)playerImgIndex = 24;
        if (jcnt > cntmax) {
            jcnt = 0;
            jflag = false;
            if (p->dir == 1)playerImgIndex = 27;
            else if (p->dir == -1)playerImgIndex = 25;
        }
    }
    if (onGround == true && CheckHitKey(KEY_INPUT_W)) {
        jflag = true;
    }
    
    //プレイヤーショットの発射処理
    if (Key_prev(mouse,KEY_INPUT_LEFT))
    {
        if (p->dir == -1) {
            PL_Shot_Main();
        };
        if (p->dir == 1) {
            PL_Shot_Main2();
        };
        
    }
   
   
    /*重力加算*/
    Player.speed.y += gravity;

    //位置に速度を加算して移動
    p->pos.x += p->speed.x;
    p->pos.y += p->speed.y;

    //速度チェック
    if (p->speed.y <= -15) {
        p->speed.y = -15;
    };
    //足元判定
    if (p->pos.y >= 592) {
        gravity = 0;
        p->speed.y = 0;
        p->pos.y = 592;
        onGround = true;
    }
    else {
        onGround = false;
    };
    
    //エリアチェック
    if (p->pos.x < 0)p->pos.x = 0;
    if (p->pos.x + 64>1300)p->pos.x = 1236;
    
};

void PlayerDraw(OBJ2D*p)
{
    DrawGraph(p->pos.x, p->pos.y, handle.image.Player[playerImgIndex], true);
};

void PlayerAnim() {
    //移動モーション
    // カーソルキーの左右どちらかが押されていて地上にいたら
    if ((CheckHitKey(KEY_INPUT_A) || (CheckHitKey(KEY_INPUT_D)))&&onGround)
    {
        ++count;
        if(Player.dir==1)playerImgIndex = (count % 8) + 16; // countを8で割った余り +8、つまり8~15が入る
        else if(Player.dir==-1)playerImgIndex = (count % 8) + 8; // countを9で割った余り +8、つまり16~23が入る
    }
};

void Playerwait()
{
    //待機モーション
    if ((CheckHitKey(KEY_INPUT_D) == false && CheckHitKey(KEY_INPUT_A) == false) && onGround)
    {
        ++count;
        if(Player.dir==-1)playerImgIndex = count % 4;
        else if (Player.dir == 1)playerImgIndex = (count % 4) + 4;
        
    };
};

bool Key_prev(const int Key, const int num)
{
    static bool KeyCheck = false;

    if (Key&num) 
    {

        if (!KeyCheck)
        {
            KeyCheck = true;
            return true;
        }
    }
    else {
        KeyCheck = false;
    }
    return false;
};
↓PL_Shot.cpp

コード:

//******************************************************************************
//
//
//		PL_Shot.cpp
//
//
//******************************************************************************

//--------------------------------
//	インクルード
//--------------------------------
#include "All.h"

//--------------------------------
//	定数
//--------------------------------

//--------------------------------
//	構造体
//--------------------------------

//--------------------------------
//	変数
//--------------------------------
SHOT pl_shot[PL_SHOT_MAX];
extern OBJ2D Player;
//--------------------------------
//	初期化
//--------------------------------
void PL_Shot_Init()
{
    //SHOT構造体の配列pl_shotを初期化
    ZeroMemory(pl_shot, sizeof(pl_shot));//pl_shotの中身を全て0にする
}

//--------------------------------
//	更新
//--------------------------------
void PL_Shot_Update()
{
    int i;
    for (i = 0; i < PL_SHOT_MAX; i++) {
        if (!pl_shot[i].exist) continue;//!を使用する方が短く記述できる

        //プレイヤーショットの移動
        pl_shot[i].x += pl_shot[i].speed_x;//x方向の位置にx方向の速度を足す

        //プレイヤーショットのエリア外チェック
        if (pl_shot[i].x < 0) pl_shot[i].exist = false;

        if (pl_shot[i].x > SCREEN_WIDTH)
            pl_shot[i].exist = false;
        PL_Shot_Draw();
    }
}

//--------------------------------
//	描画
//--------------------------------
void PL_Shot_Draw()
{
   
    //プレイヤーショットの描画
    int i;
    for (i = 0; i < PL_SHOT_MAX; i++) {
        if (!pl_shot[i].exist) continue;//存在しないものはcontinue

        const int X = pl_shot[i].x;//x座標
        const int Y = pl_shot[i].y;//y座標
        if (Player.dir == -1) {
            //const int Data = handle.image.PL_Shot[i];//画像ハンドル
            const int Data = handle.image.prot;//画像ハンドル
            DrawGraph(X, Y, Data, TRUE);
        };
        if (Player.dir == 1) {
            const int Data = handle.image.PL_Shot2[i];//画像ハンドル
            DrawGraph(X, Y, Data, TRUE);
        };

        
    }
}

//--------------------------------
//	プレイヤーショットの生成
//--------------------------------
void PL_Shot_Set(float x,float y,float speed)
{
    int i;
    for (i = 0; i < PL_SHOT_MAX; i++) {
        if (pl_shot[i].exist) continue;
        ZeroMemory(&pl_shot[i], sizeof(SHOT));//pl_shot[i]の中身を全て0にする   
        pl_shot[i].x = x;
        pl_shot[i].y = y;
        pl_shot[i].speed_x = speed;//SHOT_SPEEDではない
        
        pl_shot[i].exist = true;
        break;
    }
}
//右向き
void PL_Shot_Main()
{
  
    PL_Shot_Set(Player.pos.x + 84, Player.pos.y + 64, PL_SHOT_SPEED);
   
};
//左向き
void PL_Shot_Main2()
{
    
    PL_Shot_Set(Player.pos.x - 20, Player.pos.y + 64, -PL_SHOT_SPEED);

};
↓Loading.h

コード:

#pragma once

struct  Image
{
    int Player[40];
    int PL_Shot[5];
    int PL_Shot2[5];
    int prot;
};

struct BGM
{

};

struct SE
{

};

struct Handle 
{
    Image image;
    SE se;
    BGM bgm;
};

extern void load();
extern Handle handle;
extern Handle se;
extern Handle bgm;
↓PL_Shot.h

コード:

#pragma once
//--------------------------------
//	定数
//--------------------------------
#define PL_SHOT_MAX		(5)//プレイヤーショットの最大数
#define PL_SHOT_SPEED	(20)//1フレームあたりに移動するスピード


//--------------------------------
//	構造体
//--------------------------------
struct SHOT {
    float x;//位置x
    float y;//位置y
    float angle;//角度
    float speed_x;//横方向の速度
    float speed_y;//縦方向の速度
    bool exist;//存在フラグ
};

//--------------------------------
//	ワークエリア用構造体
//--------------------------------

//--------------------------------
//	変数
//--------------------------------
extern SHOT pl_shot[PL_SHOT_MAX];

//--------------------------------
//	関数
//--------------------------------
void PL_Shot_Init();
void PL_Shot_Update();
void PL_Shot_Draw();
void PL_Shot_Set(float, float, float);
void PL_Shot_Main();
void PL_Shot_Main2();
 [1.3] どのようなエラーやトラブルで困っているか(エラーメッセージが解る場合は記載)
実行自体はできるのですが弾を発射した際に途中で弾の大きさが変化してしまいます。
 [1.4] 今何がわからないのか、知りたいのか
  弾の描画が画像道理に飛ぶ方法
[2] 環境  
 [2.1]Windows
 [2.2]C++,Dxlib
[3] その他
 ・どの程度C言語を理解しているか
ゲームを2本ほど作りました。
  if文やfor文などの基本的な使い方は知っています。
 ・ライブラリを使っている場合は何を使っているか
   Dxlib

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 弾の描画について

#2

投稿記事 by みけCAT » 5年前

以下の仮ソースコードと添付の仮画像を追加して実行してみたところ、
一部の弾が1フレームに移動する距離(20px)だけずれて二重に描画される現象が観測できました。
また、同じ弾でも飛んでいる途中に二重になったり普通になったりすることもありました。
原因は調査中です。

仮ソースコード
ALL.h

コード:

#ifndef ALL_H_GUARD_BE6CB1AC_7161_4AE9_9B9E_748D0EEC3A87
#define ALL_H_GUARD_BE6CB1AC_7161_4AE9_9B9E_748D0EEC3A87

#include <DxLib.h>
#include "Loading.h"
#include "PL_Shot.h"

static const int SCREEN_WIDTH = 1280;

void PlayerInit();
void PlayerUpdate();
extern int count, playerImgIndex, timer, WaitAnimTimer;

struct coord {
	int x, y;
};

struct OBJ2D {
	int exist, mvAlg, dir;
	coord pos, speed;
};

#endif
haribote.cpp

コード:

#include "All.h"

Handle handle;

void load() {
	LoadDivGraph("Player.png", 40, 8, 5, 32, 32, handle.image.Player);
	LoadDivGraph("PL_Shot.png", 5, 5, 1, 32, 32, handle.image.PL_Shot);
	LoadDivGraph("PL_Shot2.png", 5, 5, 1, 32, 32, handle.image.PL_Shot2);
	handle.image.prot = LoadGraph("prot.png");
}
添付ファイル
screenshot1-20181105.png
二重に描画されたりされなかったり
screenshot1-20181105.png (52.05 KiB) 閲覧数: 6970 回
kari_images.zip
仮画像
(5.76 KiB) ダウンロード数: 235 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 弾の描画について

#3

投稿記事 by みけCAT » 5年前

さらに観察したところ、
生きている弾の中でpl_shotのインデックスが最小のものが普通に、その他が二重に描画されるようです。
原因は調査中です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 弾の描画について

#4

投稿記事 by みけCAT » 5年前

原因がわかりました。
全ての弾の描画を行うPL_Shot_Draw()をPL_Shot_Update()内のループの中で呼んでしまっているため、
処理順で最初の弾以外は座標更新前に1回以上描画され、座標更新後にも描画されるので、
二重になってしまうようです。
よって、PL_Shot_Draw()をPL_Shot_Update()内のループ内ではなく、
ループから出た後で呼ぶようにすると改善するでしょう。

また、PlayerDraw()もPlayerUpdate()内のループの中から呼ばれていますが、
こちらはループが1回しか回らない上、引数で処理対象を指定しているので、問題ないでしょう。

そもそも、~Update()内で~Draw()を呼ぶのは間違いでは無いかもしれませんが、個人的には疑問です。
~Update()内で~Draw()を呼ぶのではなく、WinMain内で一連の~Update()を呼んだ後で一連の~Draw()を呼ぶ、
という設計にするのもいいかもしれません。
オフトピック
ちなみに、弾の発射後でも自機とマウスカーソルの位置関係によって弾の画像が動的に変わるのは…
仕様でしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 弾の描画について

#5

投稿記事 by みけCAT » 5年前

ちなみに、今回は追加した仮ソースコードを以下のように変更することで、
一部の関数の呼び出しのログを取り、異常な描画関数の呼び出しを発見しました。

ALL.h

コード:

#ifndef ALL_H_GUARD_BE6CB1AC_7161_4AE9_9B9E_748D0EEC3A87
#define ALL_H_GUARD_BE6CB1AC_7161_4AE9_9B9E_748D0EEC3A87

#include <DxLib.h>
#include "Loading.h"
#include "PL_Shot.h"

static const int SCREEN_WIDTH = 1280;

void PlayerInit();
void PlayerUpdate();
extern int count, playerImgIndex, timer, WaitAnimTimer;

struct coord {
	int x, y;
};

struct OBJ2D {
	int exist, mvAlg, dir;
	coord pos, speed;
};

int DrawGraph_check(int x, int y, int g, int f);
#define DrawGraph DrawGraph_check
int ScreenFlip_check();
#define ScreenFlip ScreenFlip_check
int ClearDrawScreen_check();
#define ClearDrawScreen ClearDrawScreen_check

#endif
haribote.cpp

コード:

#include "All.h"
#include <stdio.h>

#undef DrawGraph
#undef ScreenFlip
#undef ClearDrawScreen

Handle handle;
FILE* fp;

void load() {
	LoadDivGraph("Player.png", 40, 8, 5, 32, 32, handle.image.Player);
	LoadDivGraph("PL_Shot.png", 5, 5, 1, 32, 32, handle.image.PL_Shot);
	LoadDivGraph("PL_Shot2.png", 5, 5, 1, 32, 32, handle.image.PL_Shot2);
	handle.image.prot = LoadGraph("prot.png");
	fp = fopen("debug_log.txt", "w");
}

int DrawGraph_check(int x, int y, int g, int f) {
	if (fp != NULL) {
		fprintf(fp, "DrawGraph(%d, %d, %d, %d)\n", x, y, g, f);
		fflush(fp);
	}
	return DrawGraph(x, y, g, f);
}

int ScreenFlip_check() {
	if (fp != NULL) {
		fputs("ScreenFlip()\n", fp);
		fflush(fp);
	}
	return ScreenFlip();
}

int ClearDrawScreen_check() {
	if (fp != NULL) {
		fputs("ClearDrawScreen()\n", fp);
		fflush(fp);
	}
	return ClearDrawScreen();
}
取得されたログの一部 (描画関数が異常に何度も呼ばれ、x座標が順に更新されていることがわかる)

コード:

ClearDrawScreen()
DrawGraph(0, 592, 67436548, 1)
DrawGraph(444, 656, 70713395, 1)
DrawGraph(244, 656, 70713395, 1)
DrawGraph(444, 656, 70713395, 1)
DrawGraph(264, 656, 70713395, 1)
ScreenFlip()
ClearDrawScreen()
DrawGraph(0, 592, 67436548, 1)
DrawGraph(464, 656, 70713395, 1)
DrawGraph(264, 656, 70713395, 1)
DrawGraph(464, 656, 70713395, 1)
DrawGraph(284, 656, 70713395, 1)
ScreenFlip()
ClearDrawScreen()
DrawGraph(0, 592, 67436548, 1)
DrawGraph(484, 656, 70713395, 1)
DrawGraph(284, 656, 70713395, 1)
DrawGraph(84, 656, 70713395, 1)
DrawGraph(484, 656, 70713395, 1)
DrawGraph(304, 656, 70713395, 1)
DrawGraph(84, 656, 70713395, 1)
DrawGraph(484, 656, 70713395, 1)
DrawGraph(304, 656, 70713395, 1)
DrawGraph(104, 656, 70713395, 1)
ScreenFlip()
ClearDrawScreen()
DrawGraph(0, 592, 67436548, 1)
DrawGraph(504, 656, 70713395, 1)
DrawGraph(304, 656, 70713395, 1)
DrawGraph(104, 656, 70713395, 1)
DrawGraph(504, 656, 70713395, 1)
DrawGraph(324, 656, 70713395, 1)
DrawGraph(104, 656, 70713395, 1)
DrawGraph(504, 656, 70713395, 1)
DrawGraph(324, 656, 70713395, 1)
DrawGraph(124, 656, 70713395, 1)
ScreenFlip()
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

エミリア

Re: 弾の描画について

#6

投稿記事 by エミリア » 5年前

返信ありがとうございます。
~draw関数を~update関数のループの外で呼ぶことで正しく動作しました。
オフトピックの件ですがご指摘いただくまで気づきませんでした。
消えるまで弾の画像は変わらないのが正しい仕様ですのでこれから改善していきます。

返信

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