Created
August 12, 2023 06:21
-
-
Save voidproc/c83fa3559c57ef9b699ab0e3baa0cc89 to your computer and use it in GitHub Desktop.
OpenSiv3D v0.6.11 の Trail (TrailMotion) を利用してホーミングレーザー
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# include <Siv3D.hpp> // OpenSiv3D v0.6.11 | |
namespace | |
{ | |
// 角度差 | |
double AbsAngularDifference(double theta1, double theta2) | |
{ | |
const double d1 = Abs(theta2 - theta1); | |
const double t1 = theta1 < 0 ? theta1 + Math::TwoPi : theta1; | |
const double t2 = theta2 < 0 ? theta2 + Math::TwoPi : theta2; | |
const double d2 = Abs(t2 - t1); | |
return Min(d1, d2); | |
} | |
} | |
struct HomingLaser | |
{ | |
enum class RotateDirection | |
{ | |
Left, | |
Right, | |
}; | |
// レーザーの先端の位置 | |
Vec2 pos; | |
// 進む速さと角度 | |
Circular speed; | |
// 角度を補正する方向 | |
RotateDirection rotateDirection; | |
// これがRunningの間はプレイヤーを追尾する | |
Stopwatch timerHoming; | |
// 生成されてからの時間 | |
Stopwatch timer; | |
// 軌跡 | |
Array<TrailMotion> trails; | |
HomingLaser(const Vec2& initialPos, double initialAngle, RotateDirection rotateDir) | |
: | |
pos{ initialPos }, | |
speed{ 4.0, initialAngle }, | |
rotateDirection{ rotateDir }, | |
timerHoming{ StartImmediately::Yes }, | |
timer{ StartImmediately::Yes } | |
{ | |
// 軌跡 | |
// いい感じになるように適当に追加してみる... | |
// ガワ | |
trails << TrailMotion{} | |
.setPositionFunction([&](double t) { return pos; }) | |
.setSizeFunction([](double t) { return 48 * 0.5 * (1 + Sin(t * 3.0)); }) | |
.setScaleFunction([](double t) { return t; }) | |
.setColorFunction([](double t) { | |
const auto hsv = HSV{ Palette::Lime }; | |
return HSV{ hsv.h + t * 40, hsv.s, hsv.v }; | |
}); | |
// 芯 | |
trails << TrailMotion{} | |
.setPositionFunction([&](double t) { return pos; }) | |
.setSizeFunction([](double t) { return 24 * 0.5 * (1 + Sin(t * 3.0)); }); | |
// ギザギザ | |
trails << TrailMotion{} | |
.setPositionFunction([&](double t) { return pos + RandomVec2() * 8.0; }) | |
.setSizeFunction([](double t) { return 32 * Periodic::Triangle0_1(0.08s); }) | |
.setColor(Palette::Cyan); | |
} | |
void update(const Vec2& playerPos) | |
{ | |
// 追尾 | |
if (timerHoming.isRunning() && timerHoming.sF() < 1.2) | |
{ | |
const double angleToPlayer = Atan2(-(pos.x - playerPos.x), pos.y - playerPos.y); | |
const double angleDiff = AbsAngularDifference(angleToPlayer, speed.theta); | |
if (pos.distanceFrom(playerPos) < 128 || angleDiff < 8_deg) | |
{ | |
// プレイヤーに近づきすぎた or 狙った角度になった | |
// ので追尾をやめる | |
timerHoming.reset(); | |
} | |
else | |
{ | |
// 進む角度をプレイヤーに向ける | |
const double rotSign = (rotateDirection == RotateDirection::Right) ? 1 : -1; | |
const double rotAdd = Min(angleDiff * 0.90, Math::TwoPi / 2 * Scene::DeltaTime()); | |
speed.theta += rotSign * rotAdd; | |
} | |
} | |
// 速度調整 | |
if (timerHoming.isRunning() && timerHoming.sF() < 0.35) | |
{ | |
speed.r -= 3 * Scene::DeltaTime(); | |
} | |
else | |
{ | |
speed.r += 20.0 * Scene::DeltaTime(); | |
} | |
pos += speed; | |
// 軌跡を更新 | |
for (auto& trail : trails) | |
{ | |
trail.update(); | |
} | |
} | |
void draw() const | |
{ | |
const ScopedRenderStates2D blend{ BlendState::Additive }; | |
for (const auto& trail : trails) | |
{ | |
trail.draw(TextureAsset(U"laser")); | |
} | |
} | |
}; | |
void Main() | |
{ | |
Scene::SetBackground(ColorF{ 0.05 }); | |
// レーザー軌跡用のテクスチャ | |
TextureAsset::Register(U"laser", U"example/particle.png", TextureDesc::Mipped); | |
// レーザーのリスト | |
Array<std::unique_ptr<HomingLaser>> homingLaserList; | |
// レーザー発射間隔を管理するタイマー | |
Stopwatch timerFireLaser{ StartImmediately::Yes }; | |
// レーザー発射位置 | |
const auto enemyPos = Scene::CenterF().movedBy(0, -230); | |
while (System::Update()) | |
{ | |
// 一定間隔でレーザーを発射 | |
if (timerFireLaser.sF() > 1.0) | |
{ | |
timerFireLaser.restart(); | |
homingLaserList.push_back(std::make_unique<HomingLaser>(enemyPos, 45_deg, HomingLaser::RotateDirection::Right)); | |
homingLaserList.push_back(std::make_unique<HomingLaser>(enemyPos, -45_deg, HomingLaser::RotateDirection::Left)); | |
} | |
// プレイヤーの位置(マウスカーソル位置) | |
const auto playerPos = Cursor::PosF(); | |
// レーザーを更新 | |
for (auto& laser : homingLaserList) | |
{ | |
laser->update(playerPos); | |
} | |
// 十分に画面外に出たレーザーを削除 | |
Erase_if(homingLaserList, [&](const auto& laser) { return not Scene::Rect().stretched(256).intersects(laser->pos); }); | |
// レーザー発射位置を描画 | |
RectF{ Arg::center = enemyPos, 32 }.rotated(45_deg).draw(ColorF{ 1.0, 0.5 + 0.5 * Periodic::Jump0_1(0.6s) }); | |
// プレイヤー位置 | |
const ColorF playerColor{ 1.0, 0.5 + 0.5 * Periodic::Sine0_1(0.6s) }; | |
Shape2D::Cross(28, 2).asPolygon().rotate(45_deg).movedBy(playerPos).draw(playerColor); | |
Circle{ playerPos, 20 }.drawFrame(2, playerColor); | |
// レーザーを描画 | |
for (const auto& laser : homingLaserList) | |
{ | |
laser->draw(); | |
} | |
} | |
} |
Author
voidproc
commented
Aug 12, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment