0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Godot EngineAdvent Calendar 2023

Day 18

Godot(C#)でTrail Rendererを作る

Last updated at Posted at 2023-12-17

要約

 「ImmediateMeshを使って軌跡(Trail Renderer)を描こう」の方式説明とサンプルコード
 滾る(たぎる)漢のトレイルレンダラー

はじめに

 トレイルレンダラー。それは弾の軌跡を描くシューティングゲームの花形。これが無くてもゲームは成立しますがあればとても嬉しいものです。コンピューターの計算資源を生贄に、滾る漢の表現を手に入れましょう。

 英語情報を調べればやり方は出てくるのですが、日本語の記事が無いので紹介します。元々は3Dトレイルでしたが私が欲しいのは2Dトレイルなので最後に紹介するコードは2D用になってます。

環境:Godot4.1+ C#(.Net Framework6.0)

仕組み

 移動する弾のPositionを一定時間分、毎フレーム記録します。具体的には60fpsで2秒なら120の座標情報をリストに保持します。
次に座標情報と新旧に応じて塗りつぶす領域の範囲を小さくすることで、尾を引くような描画を行います。
 図にするとこんな感じです。

Trail Rendererの動作

 4点の座標は中心点から4方向に適当なVector2 width0, width1分移動した箇所を利用しています。これをImmediateMeshを使って書き込む事で実現します。

4頂点を求める

 SurfaceBegin()で描画開始を宣言してから、後はひたすらSurfaceAddVertex2D()で頂点情報をImmediateMeshに書き込みます。この時SurfaceBegin()の引数に Mesh.PrimitiveType.TriangleStrip を指定します。

引数 説明
PrimitiveType.Points
PrimitiveType.Lines 直線なので2の倍数で登録しないとエラー
PrimitiveType.LineStrip 2の倍数でなくともOK
PrimitiveType.Triangles 三角形なので3の倍数で登録しないとエラー
PrimitiveType.TriangleStrip 3の倍数でなくともOK

 今回は単色で塗りつぶしたいのでTriangleかつ、4つの頂点を書き込むのでTriangleStripを使用しました。実装コードでは下図右側の1,2,3,4の順に書き込み、これを繰り返しています。
 試しにPrimitiveType.LineStripでワイヤーフレーム化して観察すると、書き込む順序に応じて描画されるパターンに変化がおきている事がわかります。下図左の順でメッシュに頂点を書き込むと、進行方向によって軌跡にくびれが生じてしまいます。

そうはならんやろ

 リファレンスはこちらを参照してください。

サンプルノード

 ルートノードはテスト用にシーン実行するための単なるプレースフォルダです。
 この構成ではスクリプトが2つアタッチされています。Sprite2Dにアタッチしたスクリプトは単に弾を動かすためだけに用意しました。Transformを持っていて移動できれば何でも良いです。
 MeshInstance2DにアタッチしたスクリプトがTrailRendererの実装です。
 実際に利用する際はMeshInstance2D(とアタッチしたスクリプト)だけをシーン化した汎用Trail Rendererシーンを作成し、軌跡を発生させたいオブジェクトの子ノードにして利用すると良いでしょう。

Scene

Sprite2Dには赤い光点のようなスプライトを取り付け、画面中央に来るようルートノード相対で初期位置を変更しています。

Sprite2D

 MeshInstance2Dは初期状態だとMeshが空なので適当にメッシュを追加してください。何か設定されていれば直方体でも球でもかまいません。
 インスペクタを介して"Internal parameters"から生存時間・初期サイズ・描画色を設定可能にしてあります。

MeshInstance2D

サンプルコード

Sprite2D用(RedLaser.cs)
using Godot;
using System;

public partial class RedLaser : Sprite2D
{
	private float _degree = 0;
	private float _radius = 100;
	private Vector2 _initPos;

	public override void _Ready()
	{
		_initPos = Position;
	}

	public override void _Process(double delta)
	{
		_degree += 1;
		Vector2 vec = Vector2.Zero;
		vec.X = _radius * Mathf.Sin(Mathf.DegToRad(_degree * 1));
		vec.Y = _radius * Mathf.Sin(Mathf.DegToRad(_degree * 2));
		Position = vec + _initPos;
	}
}

MeshInstance2D用(TrialRenderer.cs)
using Godot;
using System;
using System.Collections.Generic;

public partial class TrialRenderer : MeshInstance2D
{
	[ExportGroup("Internal parameters")]
	[Export] private int _lifeTime = 120;
	[Export] private float _startWidth = 4;
	[Export] private Color _startColor = Colors.White;
	
	private ImmediateMesh _mesh;
	private List<Vector2> _points = new List<Vector2>();//コンテナはVector2が保存出来れば何でも良い

	public override void _Ready()
	{
		_mesh = new ImmediateMesh();
		this.Mesh = _mesh;
	}

	public override void _Process( double delta )
	{
		_points.Add(GlobalTransform.Origin);
		if(_points.Count > _lifeTime)
			_points.RemoveAt(0);

		_mesh.ClearSurfaces();
		_mesh.SurfaceBegin(Mesh.PrimitiveType.TriangleStrip);
		{	// SurfaceBeginとSurfaceEndの区間を明示するだけのカーリーブラケット
			// 見やすくする以上の意味はありません
			_mesh.SurfaceSetColor(_startColor);
			for(int i = 0; i < _points.Count; i++){
				float unit = _startWidth * i /_lifeTime;
				Vector2 width0 = new Vector2(unit, unit);
				Vector2 width1 = new Vector2(unit, -unit);
				
				_mesh.SurfaceAddVertex2D(ToLocal(_points[i] + width0));
				_mesh.SurfaceAddVertex2D(ToLocal(_points[i] + width1));
				_mesh.SurfaceAddVertex2D(ToLocal(_points[i] - width0));
				_mesh.SurfaceAddVertex2D(ToLocal(_points[i] - width1));
			}
		}
		_mesh.SurfaceEnd();
	}
}

動かしてみよう

 滾る漢のトレイルレンダラー!

おわりに

 機能が無ければ自作するしか無いので自作しました、というお話でした。
 実装を見ても分かる通りゴリゴリにループしてます。このように汎用的かつオーバーヘッドの高い機能はC#やGDScriptといったユーザー側コードではなく、相対的にオーバーヘッドの少ない(C++)エンジン側で機能実装してほしいものです。
 GDExtention(C++)で作る事も頭をよぎりましたがヘッダとCPPファィルの2つを作るのが最近めっきり億劫になってきたのと性能面でまだ困ってないので、エンジン側うんぬんは単なるお気持ち表明です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?