-
Notifications
You must be signed in to change notification settings - Fork 1
重力とジャンプの実装
タイトルは重力の実装ですが、加速度は考えずに等速直線運動で済ませます。
やり方は簡単でPlayerのupdate関数内部でy座標を増やすだけです。
update関数は毎ループ呼ばれているので、これでSiv3D君が下に落ちます。
void update()
{
m_position.y += 10.0;
if (Input::KeyRight.pressed)
{
m_position.x += 5.0;
}
if (Input::KeyLeft.pressed)
{
m_position.x -= 5.0;
}
}
このままだと、無限に落下し続けてしまうのでブロックに当たり判定をつけます。
まずは、Block側に点との重なっているかを判定するintersects関数を作ります。
ブロックの当たり判定 = 内部のRectFの当たり判定なので、関数の内容はRectFのintersects関数を呼ぶだけです。
class Block
{
bool intersects(const Vec2 &shape) const
{
return m_region.intersects(shape);
}
}
この時注意してほしいのが、関数の後ろにconstがついていることです。これはconstメンバ関数といい、後でconst参照渡しをしたいため付けています。今はわからなくても付けておいてください。
次にPlayer側の当たり判定を実装します。
現在、地面と接しているかを表すbool型のm_isGroundedを用意します。
そしてメンバ関数としてBlockオブジェクトを受け取り、現在どれかと当たっているか(=地面に接しているか)を確認してm_isGroundedを変更するcheckGround関数を用意します。
次にさっきupdate関数内で実装した重力を地面に接していない時だけ適用するように変更します。
// 変更がない部分は省略
class Player
{
public:
void checkGround(const Array<Block>& blocks)
{
m_isGrounded = false;
for (size_t i = 0; i < blocks.size(); i++)
{
if (blocks[i].intersects(m_position))
{
m_isGrounded = true;
}
}
}
void update()
{
if (!m_isGrounded)
{
m_position.y += 10.0;
}
}
private:
bool m_isGrounded;
};
これでBlockとPlayerに必要な機能は揃ったので、あとはMain関数のでplayerのupdate関数を呼ぶ前にcheckGround関数を呼んで地面に接しているかを判定すれば完了です。
// 変更がない箇所は省略
void Main()
{
while (System::Update())
{
player.checkGround(blocks);
player.update();
}
}
次にジャンプを実装します。
メンバ変数として残りのジャンプ時間を示すint型のm_jumpFrameを用意します。もちろん初期値は0です。
そして"キャラクターが地面に接している" かつ "ジャンプ中じゃない" 時にキーが押されるとm_jumpFrameを30に設定します。
あとはupdate関数内部でm_jumpFrameが0じゃない時(=ジャンプ中)の時にy座標を下げて、キャラを上に飛ばし、m_jumpFrameを減らします。
update関数は60fpsとすれば一秒間に60回呼ばれるはずなので、30回update関数が呼ばれると0.5秒間上昇します。
// 変更がない部分は省略
class Player
{
public:
Player() :
m_position(100, 200),
m_texture(L"Example/Siv3D-kun.png"),
m_isGrounded(false),
m_jumpFrame(0) {}
void update()
{
if (m_isGrounded)
{
if (Input::KeySpace.clicked && m_jumpFrame <= 0)
{
m_jumpFrame = 30;
}
}
else
{
m_position.y += 10.0;
}
if (m_jumpFrame > 0)
{
m_position.y -= 20.0;
m_jumpFrame--;
}
}
private:
// 残りのジャンプ時間
int m_jumpFrame;
};
重力と当たり判定を実装しただけで一気にゲームっぽくなったと思います。
次は最後の仕上げに画面のスクロールを作ってみようと思います。
# include <Siv3D.hpp>
class Block
{
public:
// 引数のないコンストラクタも作っておくといろいろ便利.
Block() {}
Block(const RectF& region) :
m_region(region),
m_texture(L"Example/Brick.jpg") {}
// 描画以外の操作をする関数
void update()
{
// 今回は何もない
}
// 点との当たり判定を取る関数
bool intersects(const Vec2 &shape) const
{
return m_region.intersects(shape);
}
// 描画をする関数(描画操作以外行わないこと.)
void draw()
{
m_region(m_texture).draw();
}
private:
// ブロックの領域
RectF m_region;
// ブロックのテキスチャ(画像)
Texture m_texture;
};
class Player
{
public:
Player() :
m_position(100, 200),
m_texture(L"Example/Siv3D-kun.png"),
m_isGrounded(false),
m_jumpFrame(0) {}
void checkGround(const Array<Block>& blocks)
{
m_isGrounded = false;
for (size_t i = 0; i < blocks.size(); i++)
{
if (blocks[i].intersects(m_position))
{
m_isGrounded = true;
}
}
}
// 描画以外の操作をする関数
void update()
{
if (m_isGrounded)
{
if (Input::KeySpace.clicked && m_jumpFrame <= 0)
{
m_jumpFrame = 30;
}
}
else
{
m_position.y += 10.0;
}
if (m_jumpFrame > 0)
{
m_position.y -= 20.0;
m_jumpFrame--;
}
if (Input::KeyRight.pressed)
{
m_position.x += 5.0;
}
if (Input::KeyLeft.pressed)
{
m_position.x -= 5.0;
}
}
// 描画をする関数(描画操作以外行わないこと.)
void draw()
{
RectF(m_position.x - 72.5, m_position.y - 200, 145, 200)(m_texture).draw();
}
private:
// プレイヤーの座標
Vec2 m_position;
// プレイヤーのテクスチャ(画像)
Texture m_texture;
// 地面に接しているか否か
bool m_isGrounded;
// 残りのジャンプ時間
int m_jumpFrame;
};
void Main()
{
Window::Resize(1280, 720);
Texture background(L"Example/Windmill.png");
Player player;
Array<Block> blocks;
blocks.push_back(Block({-400, 400, 200, 200}));
blocks.push_back(Block({-200, 400, 200, 200}));
blocks.push_back(Block({0, 400, 200, 200}));
blocks.push_back(Block({200, 400, 200, 200}));
blocks.push_back(Block({200, 200, 200, 200}));
blocks.push_back(Block({400, 400, 200, 200}));
blocks.push_back(Block({800, 400, 200, 200}));
blocks.push_back(Block({1000, 400, 200, 200}));
blocks.push_back(Block({1300, 200, 400, 30}));
while (System::Update())
{
for (size_t i = 0; i < blocks.size(); i++)
{
blocks[i].update();
}
player.checkGround(blocks);
player.update();
// 実際には縦横比を合わせるように.
Rect(Window::Size())(background).draw();
for (size_t i = 0; i < blocks.size(); i++)
{
blocks[i].draw();
}
player.draw();
}
}
Written by あさちゅん
- Siv3D の基本
- 図形を描く
- テクスチャを描く
- テキストを描く
- 文字列と数値の変換
- キーボード入力
- マウス入力
- サウンドの再生
- MIDI の再生
- ウィンドウと背景
- 図形のあたり判定
- 乱数
- ダイアログ
- ドラッグ & ドロップ
- アプリの状態
- テキストファイル
- INI, CSV, JSON
- バイナリファイル
- GUI
- アセット管理
- 画像編集
- Web カメラ
- マイク入力
- 経過時間の測定
- HSV カラー
- ファイルダウンロード
- 3D 描画
- 2D のレンダーステート
- 3D のレンダーステート
- パーティクル
- スクリーンショット
- アプリケーションの公開
- さらに学ぶには
- アプリランチャーを作ろう
- 音楽プレイヤーを作ろう
- 横スクロールゲームを作ろう
- ドット絵エディタを作ろう
- シーン遷移をサポートする SceneManager の使い方
- Siv3D ミニサンプル集
- タスクシステムを使う
- スケッチ
- 画像ビューアー
- オーディオスペクトラム
- マイク入力スペクトラム
- 文字色の反転
- 天気予報
- ドットお絵かき
- 15パズル
- ブロックくずし
- 時計
- 音楽プレイヤー
- ピアノ
- ライフゲーム
- シーン管理
- 地球
- 3Dシーン
- 3D交差判定
- Wooden Mirror
- シューティングゲーム
- Image to Polygon
- Sketch to Polygon
- 軌跡
- Plot3D
- テンポとピッチの変更
- 長方形の影
- Twitterクライアント
- Polygon to Mesh
- 3Dテキスト
- アプリ終了の確認
- 地形の生成
- アーカイブファイル
- GUIのアニメーション
- Aero Glassエフェクト
- Glitch
- リンクテキスト
- 付箋
- シーン切り替え(シルエット)
- MIDIシーケンサー
- 数つなぎ
- 画面を揺らす
- 対称定規
- aobench
- MIDIビジュアライザー
- 電卓
- 手書き文字認識
- 顔検出
- 音声合成
- Image to PhysicsBody