四聖龍神録2の5章で実装している背景を実装してみます。
出来上がりはこんな感じです。
と言ってもこの章では特に内容を解説するつもりがありません。
と言うのも私が実装している手法はDXライブラリがまだ3D対応していない頃のもので、
現在は3Dに完全に対応しているので、いちいちポリゴン作って自分で貼って、全ての移動計算を自分でやるなんてもう必要ないのです。
背景になるような大きなモデルをメタセコ等の3Dモデリングソフトで作り、
それをスクロールする方がずっと楽に実装できます。
DXライブラリ愛好家の中でバトーキン島という3Dモデルを配布している方がいらっしゃいます。
このようなバトーキン島のようなモデルを作ってやり、プログラムではただスクロールするだけにした方がずっと楽に実装できます。
ただ私は3Dモデリングの知識がないためデータが配布できず、龍神録2プログラミングの館ではレガシーなやり方になっています。
従って以下のような面倒な作業を本来する必要はなく、3Dモデルを使えばずっと楽に実装出来ることを念頭に置いた上でご覧ください。
細かいことは説明しません。
まずポリゴンを作ってテクスチャを張り付けるサーフェスのクラスです。
Surface.h
#pragma once #include <DxLib.h> class Surface { public: enum eSurface { WIND, FLOR, DOOR, LOOF, DATANUM, }; Surface( int z, int sHdl, unsigned uType ); bool update(); void draw(VECTOR offset); float getZ(); float getZWid(); void addZ( float fZ ); private: VERTEX3D _vertex[4]; WORD _index[6]; int _handle; VECTOR _angle; };
Surface.cpp
#include "Surface.h" #include "Define.h" const static float WIDX = 40.0f; const static float WIDY = 40.0f; const static float WIDZ = 40.0f; const static float SCRSPD = 0.1f; const static float SPACE = 5; const static float SPD = 0.01f; const static float csDefRotaX = (-Define::PI/5.f); const static VECTOR vMOVE={0,5,-50}; const static VECTOR gvPos[Surface::DATANUM][4] = { {{0,WIDY,0},{0,WIDY,WIDZ},{0,0,WIDZ},{0,0,0}}, {{0,0,0},{0,0,WIDZ},{WIDX,0,WIDZ},{WIDX,0,0}}, {{WIDX,WIDY,0},{WIDX,WIDY,WIDZ},{WIDX,0,WIDZ},{WIDX,0,0}}, {{0,WIDY,0},{0,WIDY,WIDZ},{WIDX,WIDY,WIDZ},{WIDX,WIDY,0}}, }; Surface::Surface( int z, int sHdl, unsigned uType ) { VECTOR vPos[DATANUM][4]={}; for( int i=0; i<DATANUM; i++ ){ for( int j=0; j<4; j++ ){ vPos[i][j].x = gvPos[i][j].x - WIDX/2; vPos[i][j].y = gvPos[i][j].y - WIDY/2; } } unsigned Tp = uType; _handle = sHdl; _angle = VGet( -SPD, 0.0f, -SPD ); _vertex[ 0 ].pos = VGet( vPos[Tp][0].x, vPos[Tp][0].y, vPos[Tp][0].z+WIDZ*z ) ; //左上 _vertex[ 0 ].norm = VGet( 1.0f, 0.0f, 0.0f ) ; _vertex[ 0 ].dif = GetColorU8(255,255,255,255); _vertex[ 0 ].spc = GetColorU8( 0, 0, 0, 0); _vertex[ 0 ].u = 0.0f ; _vertex[ 0 ].v = 0.0f ; _vertex[ 0 ].su = 0.0f ; _vertex[ 0 ].sv = 0.0f ; _vertex[ 1 ].pos = VGet( vPos[Tp][1].x, vPos[Tp][1].y, vPos[Tp][1].z+WIDZ*(z+1) ); //右上 _vertex[ 1 ].norm = VGet( 0.0f, 1.0f, 0.0f ) ; _vertex[ 1 ].dif = GetColorU8(255,255,255,255) ; _vertex[ 1 ].spc = GetColorU8( 0, 0, 0, 0) ; _vertex[ 1 ].u = 1.0f ; _vertex[ 1 ].v = 0.0f ; _vertex[ 1 ].su = 0.0f ; _vertex[ 1 ].sv = 0.0f ; _vertex[ 2 ].pos = VGet( vPos[Tp][2].x, vPos[Tp][2].y, vPos[Tp][2].z+WIDZ*(z+1) ); //右下 _vertex[ 2 ].norm = VGet( -1.0f, 0.0f, 0.0f ) ; _vertex[ 2 ].dif = GetColorU8(255,255,255,255) ; _vertex[ 2 ].spc = GetColorU8( 0, 0, 0, 0) ; _vertex[ 2 ].u = 1.0f ; _vertex[ 2 ].v = 1.0f ; _vertex[ 2 ].su = 0.0f ; _vertex[ 2 ].sv = 0.0f ; _vertex[ 3 ].pos = VGet( vPos[Tp][3].x, vPos[Tp][3].y, vPos[Tp][3].z+WIDZ*z ); //左下 _vertex[ 3 ].norm = VGet( 0.0f, -1.0f, 0.0f ) ; _vertex[ 3 ].dif = GetColorU8(255,255,255,255) ; _vertex[ 3 ].spc = GetColorU8( 0, 0, 0, 0) ; _vertex[ 3 ].u = 0.0f ; _vertex[ 3 ].v = 1.0f ; _vertex[ 3 ].su = 0.0f ; _vertex[ 3 ].sv = 0.0f ; // 2ポリゴン分のインデックスデータをセット _index[ 0 ] = 0 ; _index[ 1 ] = 1 ; _index[ 2 ] = 2 ; _index[ 3 ] = 2 ; _index[ 4 ] = 3 ; _index[ 5 ] = 0 ; } bool Surface::update() { for(int i=0; i<4; i++){ _vertex[i].pos.z -= SCRSPD; } return true; } void Surface::draw(VECTOR offset) { MATRIX Matrix = MGetRotX( csDefRotaX ) ; VERTEX3D tmpVertex[ 4 ]; memcpy( tmpVertex, _vertex, sizeof( _vertex ) ); for( int i=0; i<4; i++ ){ tmpVertex[i].pos = VTransform( tmpVertex[i].pos, Matrix ) ; tmpVertex[i].pos = VAdd(tmpVertex[i].pos, vMOVE); tmpVertex[i].pos = VAdd(tmpVertex[i].pos, offset); } DrawPolygonIndexed3D( tmpVertex, 4, _index, 2, _handle, TRUE ) ; } float Surface::getZ() { return _vertex[0].pos.z; } float Surface::getZWid() { return WIDZ; } void Surface::addZ( float fZ ) { for(int i=0; i<4; i++){ _vertex[i].pos.z += fZ; } }
1面の通常背景であるBackground01クラスからこれを呼び出します。
Background01.h
#pragma once #include <list> #include <memory> #include <array> #include "Surface.h" #include "AbstractBackground.h" class Background01 final : public AbstractBackground { public: Background01(); ~Background01() = default; bool update() override; void draw() const override; private: void draw(VECTOR offset) const; std::list<std::shared_ptr<Surface>> _list; std::array<int, Surface::DATANUM> _handle; };
Background01.cpp
#include "Background01.h" #include "Define.h" #include "Image.h" #include <DxLib.h> using namespace std; const static int START_N = -1; const static int END_N = 7; Background01::Background01() { _handle[0] = Image::getIns()->getFusuma(); _handle[1] = Image::getIns()->getFloor(); _handle[2] = _handle[0]; _handle[3] = 0; for (int t = 0; t<Surface::DATANUM; t++) { for (int z = START_N; z<END_N; z++) { _list.push_back(make_shared<Surface>(z, _handle[t], t)); } } { SetCameraNearFar(1.0f, 10000.f); //カメラの有効範囲を設定 SetCameraScreenCenter(Define::OUT_W / 2.f, Define::OUT_H / 2.f); SetCameraPositionAndTarget_UpVecY(VGet(0.0f, 0.0f, -100.0f), VGet(0.0f, 0.0f, 0.0f)); SetFogEnable(TRUE); SetFogColor(0, 0, 0); SetFogStartEnd(0.0f, 300.0f); } { MATERIALPARAM Material; Material.Diffuse = GetColorF(0.0f, 0.0f, 0.0f, 1.0f); Material.Specular = GetColorF(0.0f, 0.0f, 0.0f, 0.0f); Material.Ambient = GetColorF(1.0f, 1.0f, 1.0f, 1.0f); Material.Emissive = GetColorF(1.0f, 1.0f, 1.0f, 0.0f); Material.Power = 20.0f; SetMaterialParam(Material); SetMaterialUseVertSpcColor(FALSE); SetMaterialUseVertDifColor(FALSE); } } bool Background01::update() { for (auto srf : _list) { srf->update(); if (srf->getZ() < srf->getZWid()*(START_N - 1)) { srf->addZ((END_N - START_N) * srf->getZWid()); } } return true; } void Background01::draw() const { VECTOR offset;//まだ何もない。画面を揺らすようの物 offset.x = offset.y = offset.z = 0; draw(offset); } void Background01::draw(VECTOR offset) const { SetDrawMode(DX_DRAWMODE_BILINEAR); for (auto srf : _list) { srf->draw(offset); } SetDrawMode(DX_DRAWMODE_NEAREST); }
GameSceneは通常背景・スペル背景の2種類同時に持っておきます。
弾幕が通常の時は通常の背景のインスタンスをupdate、drawし、
弾幕がスペルの時はスペルの背景のインスタンスをupdate、drawすればよいです。
GameScene.h
#pragma once #include "AbstractScene.h" #include "Player.h" #include <memory> #include "Board.h" #include "AbstractBackground.h" class GameScene : public AbstractScene { public: const static char* ParameterTagStage;//パラメータのタグ「ステージ」 const static char* ParameterTagLevel;//パラメータのタグ「レベル」 GameScene(IOnSceneChangedListener* impl, const Parameter& parameter); virtual ~GameScene() = default; void update() override; void draw() const override; private: std::shared_ptr<Player> _player; std::shared_ptr<Board> _board; std::shared_ptr<AbstractBackground> _background; std::shared_ptr<AbstractBackground> _backgroundSpell; };
GameScene.cpp
#include "GameScene.h" #include <DxLib.h> #include "Macro.h" #include "Background01.h" #include "Background01spell.h" using namespace std; const char* GameScene::ParameterTagStage = "ParameterTagStage";//パラメータのタグ「ステージ」 const char* GameScene::ParameterTagLevel = "ParameterTagLevel";//パラメータのタグ「レベル」 GameScene::GameScene(IOnSceneChangedListener* impl, const Parameter& parameter) : AbstractScene(impl, parameter) { _background = make_shared<Background01>(); _backgroundSpell = make_shared<Background01spell>(); _player = make_shared<Player>(); _board = make_shared<Board>(); } void GameScene::update() { _background->update(); _player->update(); _board->update(); } void GameScene::draw() const { _background->draw(); _player->draw(); _board->draw(); }