Siv3Dのサンプルソースコードです。
サンプルのシューティングゲームを擬似3Dにしてみました。
3Dカメラパラメータをうまく設定することで
3Dオブジェクトを2D座標のように指定しても
だいたい位置と大きさが合うことが特徴です。
// @file Main.cpp
// @date 2015-05-02
// サンプル シューティングゲームを
// 擬似3Dにしてみた。
// 元ソースは以下。
// http://play-siv3d.hateblo.jp/entry/jp/example/shooting
#include <Siv3D.hpp>
#include "HamFramework\SceneManager.hpp"
Image CreateSkyImage() {
Noise noise;
Image image(640, 960);
for (int y = 0; y < image.height; ++y)
for (int x = 0; x < image.width; ++x)
image[y][x] = HSV(220, 0.5 + 0.4 * noise.octaveNoise(x / 160.0, Abs(y / 120.0 - 4.0), 8), 0.8);
return image;
}
struct Data {
int score = 0;
int highScore = 0;
int crash = 0;
Array<Vec2> shots, bullets, enemies;
Texture texture;
Font font;
Triangle player;
};
using MyApp = ham::SceneManager < String, Data >;
struct MainScene : MyApp::Scene {
int count = 0;
bool pseudo3D = true;
void init() override {
Window::SetTitle(L"Siv Shooting 擬似3D化 | [X]: 切り替え / [Z]: shot / 十字キー: 移動");
m_data->texture = Texture(CreateSkyImage());
m_data->font = Font(20);
m_data->player = Triangle(300, 200, 20.0);
// 640x480の場合に近似するためのカメラ配置
Camera camera;
camera.pos = { 320, 240, 16384 };
camera.lookat = { 320, 240, 0 };
camera.up = Vec3::Down;
camera.fovDegree = 1.68;
Graphics3D::SetCamera(camera);
Graphics3D::SetAmbientLight(ColorF(0.25, 0.25, 0.25));
Graphics3D::SetLight(0, Light::Directional(Vec3(-1, -1, 1)));
}
void update() override {
++count;
if (Input::KeyX.clicked) {
pseudo3D = !pseudo3D;
}
if (count % (24 - Min(count / 60, 18)) == 0)
m_data->enemies.emplace_back(Random(40, 600), -40);
const Vec2 dir(Input::KeyRight.pressed - Input::KeyLeft.pressed, Input::KeyDown.pressed - Input::KeyUp.pressed);
if (!dir.isZero)
m_data->player.moveBy(dir.normalized() * (Input::KeyShift.pressed ? 4.5 : 9.0));
m_data->player.setCentroid(Clamp(m_data->player.centroid.x, 0.0, 640.0), Clamp(m_data->player.centroid.y, 0.0, 480.0));
if (Input::KeyZ.pressed && count % 4 == 0)
m_data->shots.push_back(m_data->player.p0);
for (auto& shot : m_data->shots)
shot.y -= 8.0;
for (auto& bullet : m_data->bullets)
bullet.y += 4.0;
for (auto& enemy : m_data->enemies) {
enemy.y += 2.0;
if (count % 60 == 0)
m_data->bullets.push_back(enemy);
}
// 侵略条件は厳しいのでカット
//if (AnyOf(m_data->bullets, [=](const Vec2& b){ return m_data->player.intersects(b); })
// || AnyOf(m_data->enemies, [=](const Vec2& e){ return e.y > 490.0; })) {
if (AnyOf(m_data->bullets, [=](const Vec2& b){ return m_data->player.intersects(b); })) {
count = m_data->score = 0;
m_data->crash = 60;
}
Erase_if(m_data->shots, [](const Vec2& s){ return s.y < -10.0; });
Erase_if(m_data->bullets, [](const Vec2& b){ return b.y > 490.0; });
Erase_if(m_data->enemies, [&](const Vec2& e){
if (AnyOf(m_data->shots, [=](const Vec2& s){ return e.distanceFrom(s) < 20.0; })) {
++m_data->score;
return true;
}
else return e.y > 490.0;
});
}
void draw() const override {
const int skyOffset = System::FrameCount() % 960 * 8;
m_data->texture(0, -skyOffset / 2, 640, 480).draw();
m_data->texture(0, -skyOffset, 640, 480).draw(Alpha(80));
if (pseudo3D) {
Graphics::Render2DBackground();
}
for (const auto& shot : m_data->shots) {
if (pseudo3D == false) {
Circle(shot, 7).drawFrame(4, 0, Palette::Orange);
}
else {
Sphere({ shot.x, shot.y, 0 }, 7).draw(Palette::Orange);
}
}
for (const auto& bullet : m_data->bullets) {
if (pseudo3D == false) {
Circle(bullet, 4).draw();
}
else {
Sphere({ bullet.x, bullet.y, 0 }, 4).draw();
}
}
for (const auto& enemy : m_data->enemies) {
if (pseudo3D == false) {
RectF(30, 30).setCenter(enemy).rotated(enemy.y / 100.0).draw(Palette::Black);
}
else {
double ang = enemy.y / 100.0;
double pitch = ang * 0.0;
Box({ enemy.x, enemy.y, 0 }, 30,
Quaternion::RollPitchYaw(ang,pitch,0)).draw(Palette::Dimgray);
}
}
if (pseudo3D == false) {
m_data->player.draw();
}
else {
// p0は前の1点、p1,p2は後ろの2点
Cone({ m_data->player.p0.x, m_data->player.p1.y, 0 }, 10, 10 * Sqrt(3), Quaternion::Roll(Pi)).draw();
}
if (m_data->crash)
Rect(640, 480).draw(Alpha(--m_data->crash * 3));
m_data->highScore = Max(m_data->score, m_data->highScore);
m_data->font(L"Hi:", m_data->highScore, L"\n", m_data->score).draw(20, 20);
}
};
void Main() {
MyApp myApp;
myApp.add<MainScene>(L"MainScene");
while (System::Update()) {
if (!myApp.updateAndDraw()) {
break;
}
}
}