本稿では、ゲームでキャラクターを移動させるときに必要な考え方と、具体的な計算方法について共有します。
今回はスロープ(坂道)の移動についてです。
AI に作らせて終わり…ではなく、「ゲームの仕組みを基礎からしっかりと学びたい!」というゲーム開発者を目指している人や、既にゲーム開発の仕事に就いていて、足りない部分の知見を補完したい人向けの内容です。
対象者
・ゲームのメカニズムに興味がある。
・中学校の数学の授業で習う四則演算を習得している。
・AI を使って自主的に学習を進められる。
最初の移動方法は、どうして「×」なのか?
もし、キャラクターが空中にいるなら、計算する必要はありません。
空中をフワフワと浮遊しているなら、この処理は必要ありません。
重力の仕組みがある場合、着地してから計算を始めます。
まず、着地した地面の位置(座標)を、これから先の計算で使える状態にします。
![]()
もし、キャラクターの位置を絵の中心に設定している場合(↑の画像の赤い線の位置)、キャラクターの足元の位置(↑の画像の青い線の位置)で計算する必要があります。
→ X 軸
↓ Y 軸
キャラクターの位置が (cx, cy) で、キャラクターの高さが height なら、(cx, cy + height / 2)
逆に、↑に行くほど値が大きくなる座標系なら、(cx, cy – height / 2)
足すのか、引くのかは、Y 軸の向きが上になっているか、下になっているかで変わります。
画面のピクセルを使う座標系(Webブラウザなど)は下向きで、ゲームエンジンは上向きが多いです。
キャラクターの位置を足元に設定しているなら、この計算は不要です。
キャラクターがいる現在の地面の位置は (cx, cy) で、cy は前述の足元の位置に修正済みとします。
gemini 画伯作
移動を続けた場合の、次の移動先の地面の位置 (dx, dy) を計算します。

dx = cx + xの移動量
dx は単純に移動量を足すか、引くだけです。

dy を算出するには、地面にぶつかるまで、1ピクセルずつ「これは地面か?」と問い合わせる必要があります。
ただ、今のゲーム制作では、座標を1ピクセル単位で計算することはほとんどありません。
自分でその仕組みを作れば可能ですが、既存のゲームエンジンでそれをやるのは、けっこうめんどくさいです。

gemini 画伯作
今のゲーム制作では、座標は 3D のバーチャルな空間を使います。
そこに地面や壁などを配置して、キャラクターがスポーン(出現)する位置を設定します。
2D ゲームの場合も、計算は 3D 空間で、その 3D 空間を 2D 用のカメラで映すことで 2D ゲームの見た目になるという仕組みです。

gemini 画伯作
既存のゲームエンジンのような仕組みでは、レイキャストという機能を使うことで、キャラから地面までの距離を調べることができます。
レイキャストの「レイ」はレーザーみたいな、まっすぐどこまでも伸びていく光線と同じです。
光線を画面に表示することもできますが、通常は何も表示されません。
このレイは計算用です。

gemini 画伯作
キャラから地面にまっすぐレイを飛ばして、レイが地面に当たったときに、キャラからレイが当たった地面までの距離が分かります。
例えば、Unity や Unreal Engine などのゲームエンジンには、レイキャストがあるので、それを使うだけです。
レイキャストにはベクトルの計算が必要になりますが、「ベクトルが何か?」を知っている必要はありません。
※ベクトル(vector)は高等学校の数学で習います。
ベクトルについて詳しく知りたいなら、AI が教えてくれます。

gemini 画伯作
以下は AI が提示してくれた Unity と Unreal Engine 用のサンプルコードです。
※コードをざっと見て問題なさそうなので掲載していますが、動作確認まではしていません。
// Unity 用
using UnityEngine;
public class GroundCheckUnity : MonoBehaviour
{
[SerializeField] private float rayLength = 2.0f; // レイの長さ
[SerializeField] private LayerMask groundLayer; // 地面のレイヤーを指定する場合
void Update()
{
// キャラクターの足元をスタート地点にする
Vector3 rayStart = transform.position;
Vector3 rayDirection = Vector3.down; // 真下
// レイキャストの衝突情報を格納する構造体
RaycastHit hit;
// レイを可視化(デバッグ用)
Debug.DrawRay(rayStart, rayDirection * rayLength, Color.red);
// レイキャスト実行 (位置, 方向, 衝突結果, 長さ, レイヤーマスク)
if (Physics.Raycast(rayStart, rayDirection, out hit, rayLength, groundLayer))
{
// 地面に当たった場合の処理
GameObject hitObject = hit.collider.gameObject;
float distanceToGround = hit.distance;
Vector3 hitPoint = hit.point;
Debug.Log($"地面を検知: {hitObject.name}, 距離: {distanceToGround}, 衝突点: {hitPoint}");
}
else
{
// 空中にいる場合など
Debug.Log("地面が見つかりません");
}
}
}
↑の distanceToGround がレイの長さ
// Unreal Engine 用
#include "MyCharacter.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h" // デバッグ描画用
void AMyCharacter::CheckGroundUnderfoot()
{
UWorld* World = GetWorld();
if (!World) { return; }
// 1. レイの開始位置と終了位置の設定
// キャラクターの足元からスタートする(カプセルの中心などからでもOK)
FVector StartLocation = GetActorLocation();
FVector EndLocation = StartLocation + FVector(0.f, 0.f, -200.f); // 200ユニット真下まで
// 2. トレースの設定(自分自身を無視するなど)
FHitResult HitResult;
FCollisionQueryParams Params;
Params.AddIgnoredActor(this); // キャラクター自身への衝突を無視
// 3. 衝突チャンネルの指定 (ここでは一般的な Visibility または Camera/Pawn などを指定)
ECollisionChannel TraceChannel = ECC_Visibility;
// 4. レイキャスト(ライントレース)の実行
bool bHit = World->LineTraceSingleByChannel(
HitResult,
StartLocation,
EndLocation,
TraceChannel,
Params
);
// 5. デバッグ用レイの描画 (エディタ上でのみ可視化)
DrawDebugLine(
World,
StartLocation,
EndLocation,
bHit ? FColor::Green : FColor::Red,
false,
-1.f,
0,
1.f
);
// 6. 結果の処理
if (bHit)
{
AActor* HitActor = HitResult.GetActor();
float DistanceToGround = HitResult.Distance;
FVector HitPoint = HitResult.ImpactPoint;
if (HitActor)
{
UE_LOG(LogTemp, Warning, TEXT("地面を検知: %s, 距離: %f, 衝突点: %s"),
*HitActor->GetName(),
DistanceToGround,
*HitPoint.ToString()
);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("地面が見つかりません"));
}
}
↑の DistanceToGround がレイの長さ
dy = cy + レイの長さ
(dx, dy) は移動先の座標なので、この座標にキャラを移動させます。
スロープの移動先の座標を算出するには、キャラの足元から地面までの距離を測る必要がある。
測り方はひとつではない。
・有名なゲームエンジンを使う場合は、レイキャスト機能があるので、それを使う。
・キャラの足元から地面までの距離が分かるなら、やり方はなんでも良い。
※ただし、ゲームの動作が重くならないよう注意。

gemini 画伯作
以下はご自身で考えてみてください。
スロープを上る場合の計算方法
スロープが途中で途切れている場合の移動方法(下図)。

以下は JavaScript で作った本稿のサンプルプログラムです。
上記サンプルのコードを見れば、どのような処理をしているかが分かります。
コードを見る方法はブラウザによって違いますが、ページのソースを表示、開発者用ツール、コンソール、デバッグなどをたどれば見つかると思います。
サンプルが使用している game.js は筆者が作成したマイクロライブラリで、以下の記事で紹介しています。
サンプルプログラムの作成では、以下の範囲で生成 AI と AI エージェントを使用しています。
Gemini
・canvas を使った仕組みの提案
・canvas を JavaScript 制御する方法の提案
※エフォートはデフォルトです。
Claude Code
・メッド内の細かい計算処理の実装。
・コードの修正
※Sonnet 5.5 中以上~Opus 5.5 低
筆者
・プログラム設計の考案
・AI への相談と作業指示
・AI が生成したコードのチェック
・動作確認
画像生成
Gemini
Claude Code
※本稿では使用していませんが、↑でうまく生成できないときは、ChatGPT や、ローカルで Qwen Image を使ったりもします。
※本稿のアイキャッチ画像は Gemini で生成したものです。
少し前までは日本語がおかしいところがあったんですが、完璧になりましたね…。
以下も AI で作った解説動画です。
本稿をベースにしている点は同じですが、音声解説とは視点が違います。














