【Odin Inspector】NPCの振る舞いをインスペクターで設定する
はじめに
こんにちは、学部3年の木村です。
タイトルの通り、ゲームに登場するNPCの行動をインスペクターで設定できるようにします。
想定するゲームにはNPCとしてMonster(獅子舞や仏像)が存在し、 各NPCは現在の状態に応じて探索や追跡などの行動をとることができます。
この場合、Dictionary
を使って状態と行動を対応付けたいのですがUnityのInspectorではGenericsを扱えないため、アクセス修飾子をpublic
にしたり属性にSerializeFieled
を付けたりしてもInspectorには表示されません。抽象クラスやインターフェースも同様です。
そこで、Odin - Inspector and Serializerというアセットを使用しました。
Odinとは
OdinはUnityのInspectorを使いやすくするプラグインです。
導入するだけでList等の表示がいい感じになったり、 (多分)何でもシリアライズできるようになったり、便利な属性が80以上使えるようになります。odininspector.comではどんなことができるようになるかがアニメーション付きで紹介されています。
$55の有料アセットですが、ゲーム制作中のパラメータ調整などが圧倒的に快適になるとても便利なアセットです。
本編
一部省略していますが、 大体以下のような構成です。
Monster
が継承しているSerializedMonoBehaviour
は、Dictionary
等もシリアライズできる(Inspectorで設定できる)MonoBehaviour
です。元のMonoBehaviour
の機能はそのまま使えます。
Inspectorでは次のように表示されます。(クラス図ではMonster
に定義しているActionsTable
がMonsterActionRepository
で定義されています。)
Dictionary
がInspectorに表示されており、Keyに状態(MonsterState
)、Valueにその状態のときにとる行動(抽象クラスNpcAction
の実装)を指定することでNPCの振る舞いを設定できます。
ついでに
OdinのButton
属性を使って、InspectorのボタンからMonsterの状態を変更できるようにしています。
public bool _enableDebug; [Button, ShowIf(nameof(_enableDebug)), EnableIf(nameof(_enableDebug))] private void ChangeState(MonsterState state) { // 状態をstateに変更 }
終わり
Odinの紹介と活用例でした。
ゲーム制作中にUnityのインスペクターを使っていると微妙に手の届かないかゆいところがあり、自分でエディタ拡張を書いていたら沼にハマり肝心のゲーム制作が進まないということがあるかもしれません。そんなときにOdinはかゆいところをいい感じにかいてくれます。
ちょっと高めのアセットですがUnityがとても快適になるので、よかったら導入してみてはいかがでしょうか。
三次元極座標系でパーティクル
学部4年のユムルです。
最近Shaderをかいたり、VRのMV (Music Video) を作ったりして遊んでます。
作った物をVRSNSのVRChatにて人に見せたりして楽しんでいるのですが、最近ブタジエン(https://twitter.com/butadiene121)さんのシェーダーパーティクルに魅せられて、真似して次のようなシェーダーパーティクルを作りました。
綺麗なパーティクルできたー!
— ユムル (@Yumuru_n) 2020年9月7日
お花くるくるー
(2週間前に作った) pic.twitter.com/IwA7mcsO9I
これの解説をします。
パーティクルとして使うメッシュと表現の決定
今回はCubeのメッシュをもとに、次の表現を使うことにします。
書いたShaderは次のものです。
Shader "PolarParticle/Cube1" { Properties { } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } // 透明 Cull off Blend SrcAlpha One ZWrite Off LOD 100 Pass { CGPROGRAM #pragma target 5.0 #pragma vertex VS #pragma fragment FS #include "UnityCG.cginc" struct VS_OUT { float4 vertex : SV_Position; float4 texcoord : TEXCOORD0; float4 color : COLOR; }; VS_OUT VS(appdata_full v) { VS_OUT o; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(0, 1, 1, 1); return o; } float4 FS(VS_OUT i) : SV_Target { float2 uv = i.texcoord.xy; float2 p = uv * 2.0 - 1.0; float t = 0.01 / min(abs(abs(p.x) - 1.0), abs(abs(p.y) - 1.0)); t = saturate(t); return float4(i.color.xyz, t); } ENDCG } } }
メッシュの大量複製
メッシュをパーティクルとして扱うために、メッシュを大量にコピーしたいです。
私はこれをするために2種類の方法を知っています。 普通は次の方法1を使えば良いと思いますが、VRChatではそれをすることはできないので方法2を使いました。
それぞれの方法で試しに100万個のCubeを表示してみますが、後につくる極座標のパーティクルは1024個で作成します。
方法1
次の記事を参考にします。 shitakami.hatenablog.com feelingames.hatenablog.com
次のコードを書きます。
using UnityEngine; public class GPUInstance : MonoBehaviour { public Mesh mesh; public Material material; public Bounds bounds; public int instanceNum; public ComputeBuffer buffer; void Start() { var args = new uint[5] { 0, 0, 0, 0, 0 }; args[0] = mesh != null ? (uint) mesh.GetIndexCount(0) : 0; args[1] = (uint) instanceNum; buffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); buffer.SetData(args); } void Update() { Graphics.DrawMeshInstancedIndirect( mesh, 0, material, bounds, buffer); } void OnDestroy() { if (buffer != null) buffer.Release(); } }
取り敢えず大量のCubeを規則正しく並べるシェーダーを書きます、
Shader "PolarParticle/ManyCube" { Properties { } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Cull off Blend SrcAlpha One ZWrite Off LOD 100 Pass { CGPROGRAM #pragma target 5.0 #pragma vertex VS #pragma fragment FS #include "UnityCG.cginc" struct VS_OUT { float4 vertex : SV_Position; float4 texcoord : TEXCOORD0; float4 color : COLOR; }; static uint particleNum = 1000000; // パーティクルの数 VS_OUT VS(appdata_base v, uint instanceId : SV_InstanceID) { VS_OUT o; uint id = instanceId; // パーティクルのID uint n = pow(particleNum, 1.0 / 3.0); float particleSize = 0.1; // パーティクルのサイズ float interval = 1; // パーティクル間の距離 float zoneLength = (n-1) * interval; // 全体の範囲の長さ uint3 axisId = uint3( // 極ごとのID id % n, id / n % n, id / (n * n)); float3 pos = (float3)(-zoneLength / 2) + axisId * interval; v.vertex.xyz = v.vertex.xyz * particleSize + pos; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(0, 1, 1, 1); return o; } float4 FS(VS_OUT i) : SV_Target { float2 uv = i.texcoord.xy; float2 p = uv * 2.0 - 1.0; float a = 0.01 / min(abs(abs(p.x) - 1.0), abs(abs(p.y) - 1.0)); a = saturate(a); return float4(i.color.rgb, a); } ENDCG } } }
書いたシェーダからマテリアルを作成し、GameObjectに先ほどのC#のコードをアタッチして、次のように100万個のCubeが出るように設定します。
シーンを再生すると、100万個のCubeの位置が頂点シェーダーによって決められているのを確認できます。
後のパーティクルの為、今回はInstance Numを1024に設定してください。
方法2
VRChatでメッシュのパーティクルを使う場合は、C#によるスクリプトを組み込んだ作品をアップロードできません。したがって、方法1も使えません。 代わりに間接的にC#を用いて、Meshを複製してアセットファイル化するスクリプトを書き、更にジオメトリシェーダーの[instance(31)]にて31倍にメッシュを増やします。
メッシュを複製したアセットファイルを作成するため、C#のエディタ拡張を書きます
using System.Linq; using System.Collections; using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using UnityEditor; public class MeshCopy : EditorWindow { [MenuItem("MyEditor/MeshCopy")] static void Open() { GetWindow<MeshCopy>("MeshCopy"); } Mesh useMesh; // コピー元のメッシュ int copyN; // 複製する数 void OnGUI() { useMesh = (Mesh) EditorGUILayout.ObjectField("Use Mesh", useMesh, typeof(Mesh), false); copyN = EditorGUILayout.IntField("Copy Num", copyN); if (GUILayout.Button("Mesh N Copy")) { MeshNCopy(); } } void MeshNCopy() { var vertices = new List<Vector3>(); var texcoord = new List<Vector3>(); var triangles = new List<int>(); var normals = new List<Vector3>(); var tangents = new List<Vector4>(); var colors = new List<Color>(); // ここのループでメッシュをコピー、texcoordのz値にidを入れておく for (int i = 0; i < copyN; i++) { vertices.AddRange(useMesh.vertices); texcoord.AddRange(useMesh.uv.Select(v => new Vector3(v.x, v.y, i))); triangles.AddRange(useMesh.triangles .Select(index => index + i * useMesh.vertexCount)); normals.AddRange(useMesh.normals); tangents.AddRange(useMesh.tangents); colors.AddRange(useMesh.colors); } var mesh = new Mesh(); mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; // indexFormatを変えておかないとtrianglesの値の上限を余裕で超えてしまう mesh.SetUVs(0, texcoord); mesh.triangles = triangles.ToArray(); mesh.normals = normals.ToArray(); mesh.tangents = tangents.ToArray(); mesh.colors = colors.ToArray(); string path = string.Format("Assets/{0}x{1}.asset", useMesh.name, copyN); AssetDatabase.CreateAsset(mesh, path); AssetDatabase.SaveAssets(); } } #endif
Unity上部のメニューにある、MyEditor -> MeshCopyを開き、Cubeのメッシュを設定し、後でメッシュを更に31倍するので、Copy Numを100万 / 31 = 約33000個 に設定しボタンを押します。
すると、Assets直下に33000個のCubeメッシュのアセットファイルが生成されます。
とはいえこの時点で、メッシュファイルのサイズは81MBになってしまうので、実用的に考えるならCube1024個で2MBくらいのものになるでしょう。
Shaderの方は次のようです。こちらもcubeを規則正しく並べておきます。 メッシュのサイズを抑え、ジオメトリシェーダのinstanceにて更に31倍までポリゴンを増やすことができます。 頂点シェーダーVSは値をそのまま返すのみにし、ジオメトリシェーダGSにて頂点位置を決定します。
Shader "PolarParticle/ManyCube2" { Properties { } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Cull off Blend SrcAlpha One ZWrite Off LOD 100 Pass { CGPROGRAM #pragma target 5.0 #pragma vertex VS #pragma geometry GS #pragma fragment FS #include "UnityCG.cginc" struct GS_OUT { float4 vertex : SV_Position; float4 texcoord : TEXCOORD0; float4 color : COLOR; }; appdata_base VS(appdata_base v) { return v; } #define instanceN 31 [instance(instanceN)] [maxvertexcount(3)] void GS(triangle appdata_base i[3], inout TriangleStream<GS_OUT> stream, uint gsid : SV_GSInstanceID) { for (int j = 0; j < 3; j++) { appdata_base v = i[j]; GS_OUT o; uint id = gsid + instanceN * v.texcoord.z; // パーティクルのIDを計算 uint n = pow(33000 * instanceN, 1.0/3.0); float particleSize = 0.1; float interval = 1; float zoneLength = (n-1) * interval; uint3 axisId = uint3( id % n, id / n % n, id / (n * n)); float3 pos = (float3)(-zoneLength / 2) + axisId * interval; v.vertex.xyz = v.vertex.xyz * particleSize + pos; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(0, 1, 1, 1); stream.Append(o); } stream.RestartStrip(); } float4 FS(GS_OUT i) : SV_Target { float2 uv = i.texcoord.xy; float2 p = uv * 2.0 - 1.0; float a = 0.01 / min(abs(abs(p.x) - 1.0), abs(abs(p.y) - 1.0)); a = saturate(a); return float4(i.color.rgb, a); } ENDCG } } }
SkinnedMeshRendererにてBoundsを設定しつつ使います。
方法1との違いは、頂点シェーダーでなくジオメトリシェーダを使うこと、idの設定に一手間加えることです。
以降のシェーダーのコードは方法1を使う場合のコードとします。もし、VRChatのために方法2を使う人は読み変えてください。
後のパーティクルの為、今回は1024個のパーティクルを表示する為に1024/16 = 64個のCubeメッシュを生成し、後でジオメトリのinstanceにて16倍するように設定してください。
極座標系によるパーティクルの位置決定
x座標、y座標をもとに、座標を決定する座標系を直交座標と言いますが、
極座標系という座標系も存在します。
極座標系は原点からの距離rと、x軸から反時計回りへの角度θによる(r, θ)で座標を決定します。
原点からのrをsinやcosなどの三角関数と共に角度によって適当に決めるようにすると、綺麗な模様が描けます
二次元の極座標系(r, θ)を直交座標系(x, y)に変換する式は次の通りです
x = r cosθ
y = r sinθ
今回のパーティクルではこれを三次元で考えます。
三次元極座標は、原点からの距離r、角度はz軸からの角度θとx軸からの角度φによる(r, θ, Φ)によって決まるようです。 今回はUnityのシェーダーで実装しますから、数学でz軸であるところはy軸、y軸のところがz軸であると考えます。 したがって、原点からの距離r、y軸からの角度θ, x軸からの角度Φで考え、 三次元直交座標系(x, y, z)への変換は次のようです。
x = r sinθ cosφ
y = r cosθ
z = r sinθ sinφ
ではシェーダを書いていきます。
パーティクルのidをもとに、パーティクルの位置を極座標系を用いて算出する関数を用意します。
最初に極座標系(r, θ, Φ)の変数と直交座標系への変換のコードを書いておきます。
#define PI UNITY_PI void particleTransform(uint id, out float3 pos) { float r = 1; float theta = 0; float phi = 0; pos = float3( r * sin(theta) * cos(phi), r * cos(theta), r * sin(theta) * sin(phi)); }
さて、二次元極座標において適当なグラフを示したときはr をθをもとに決定していましたが、
今回はidの値が元になります。
したがって、idの値を適当に変数 tに入れて扱うことにします。 `` float t = id * 0.01;
θをπ / 2にしておくと、phiの値によりxz座標上の値になります
float t = id * 0.01; float r = 1; float theta = PI / 2; float phi = t;
また、作成した関数を頂点シェーダに適応します。
VS_OUT VS(appdata_base v, uint instanceId : SV_InstanceID) { VS_OUT o; uint id = instanceId; float particleSize = 0.05; float3 pos; particleTransform(id, pos); v.vertex.xyz = v.vertex.xyz * particleSize + pos; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(0, 1, 1, 1); return o; }
θの値を適当なサイン波にしてみます。
float theta = PI / 2 + sin(5.5 * t) * 0.2;
Φの値も適当なサイン波にしてみます。
float phi = sin(t) * 3 * PI;
θのサイン波の大きさを敢えて小さくしていましたが、試しに大きくしてみます。
float theta = PI / 2 + sin(5.5 * t) * 0.7;
止まっていてもつまらないので、時間によって変化するようにします。
Φの値を時間によって変化させてみます。
float phi = sin(t) * 3 * PI + sin(t + _Time.y * 0.3) * 3;
なかなかかっこよくなってきました。
tの値、rの値も時間によって変化させてみます。
float t = id * 0.01 + _Time.y * 0.1;
float r = 0.2 + sin(3 * t);
パーティクルが全て同じ方向を向いているので、回転の設定をします。
この記事を参考に、回転の関数を書きます。行列のままの方が扱いやすいので行列のまま値の受け渡しを行います。
float3x3 rotate(float angle, float3 axis) { float3 a = normalize(axis); float s = sin(angle); float c = cos(angle); float r = 1.0 - c; return float3x3( a.x * a.x * r + c, a.y * a.x * r + a.z * s, a.z * a.x * r - a.y * s, a.x * a.y * r - a.z * s, a.y * a.y * r + c, a.z * a.y * r + a.x * s, a.x * a.z * r + a.y * s, a.y * a.z * r - a.x * s, a.z * a.z * r + c); }
また、サイズと色の設定も適当に設定します。
#define PI UNITY_PI void particleTransform(uint id, out float3 pos, out float3x3 rot, out float size, out float3 color) { // out 変数追加 float t = id * 0.01 + _Time.y * 0.1;; float r = 0.2 + sin(3 * t); float theta = PI / 2 + sin(5.5 * t) * 0.7; float phi = sin(t) * 3 * PI + sin(t + _Time.y * 0.3) * 3; pos = float3( r * sin(theta) * cos(phi), r * cos(theta), r * sin(theta) * sin(phi)); // rot, size, color設定 rot = mul(rotate(theta, float3(1, 0, 0)), rotate(phi - _Time.y * 3, float3(0, 1, 0))); size = cos(5.3 * t + _Time.y * 3.0) * 0.05; color = float3(sin(2.4 + t + _Time.y * 0.6), sin(0.7 + 2 * t - _Time.y * 0.6), sin(3.0 * t + _Time.y * 0.7)) / 2 + 0.5; }
頂点シェーダも書き変えます。
VS_OUT VS(appdata_base v, uint instanceId : SV_InstanceID) { VS_OUT o; uint id = instanceId; float particleSize = 0.05; float3 pos; float3x3 rot; float size; float3 color; particleTransform(id, pos, rot, size, color); v.vertex.xyz = mul(rot, v.vertex.xyz * size) + pos; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(color, 1); return o; }
かっこいいのができあがりました。
コードの全体は次のようです。
Shader "PolarParticle/Test" { Properties { } SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Cull off Blend SrcAlpha One ZWrite Off LOD 100 Pass { CGPROGRAM #pragma target 5.0 #pragma vertex VS #pragma fragment FS #include "UnityCG.cginc" struct VS_OUT { float4 vertex : SV_Position; float4 texcoord : TEXCOORD0; float4 color : COLOR; }; float3x3 rotate(float angle, float3 axis) { float3 a = normalize(axis); float s = sin(angle); float c = cos(angle); float r = 1.0 - c; return float3x3( a.x * a.x * r + c, a.y * a.x * r + a.z * s, a.z * a.x * r - a.y * s, a.x * a.y * r - a.z * s, a.y * a.y * r + c, a.z * a.y * r + a.x * s, a.x * a.z * r + a.y * s, a.y * a.z * r - a.x * s, a.z * a.z * r + c); } #define PI UNITY_PI void particleTransform(uint id, out float3 pos, out float3x3 rot, out float size, out float3 color) { float t = id * 0.01 + _Time.y * 0.1;; float r = 0.2 + sin(3 * t); float theta = PI / 2 + sin(5.5 * t) * 0.7; float phi = sin(t) * 3 * PI + sin(t + _Time.y * 0.3) * 3; pos = float3( r * sin(theta) * cos(phi), r * cos(theta), r * sin(theta) * sin(phi)); rot = mul(rotate(theta, float3(1, 0, 0)), rotate(phi - _Time.y * 3, float3(0, 1, 0))); size = cos(5.3 * t + _Time.y * 3.0) * 0.05; color = float3(sin(2.4 + t + _Time.y * 0.6), sin(0.7 + 2 * t - _Time.y * 0.6), sin(3.0 * t + _Time.y * 0.7)) / 2 + 0.5; } VS_OUT VS(appdata_base v, uint instanceId : SV_InstanceID) { VS_OUT o; uint id = instanceId; float particleSize = 0.05; float3 pos; float3x3 rot; float size; float3 color; particleTransform(id, pos, rot, size, color); v.vertex.xyz = mul(rot, v.vertex.xyz * size) + pos; o.vertex = UnityObjectToClipPos(v.vertex); o.texcoord = v.texcoord; o.color = float4(color, 1); return o; } float4 FS(VS_OUT i) : SV_Target { float2 uv = i.texcoord.xy; float2 p = uv * 2.0 - 1.0; float a = 0.01 / min(abs(abs(p.x) - 1.0), abs(abs(p.y) - 1.0)); a = saturate(a); return float4(i.color.rgb, a); } ENDCG } } }
A-PxLが誕生してから、今まで。
こんにちは、ぶろっくのいずです。
初代部長である秋山さん達がこの部を作ってくださった時から3年以上が経ちました。 A-PxLの成り立ちを知る人もいなくなってきた事もあり、今までの活動をまとめました。 (※この記事について修正、追記出来る部員の方がいましたら気軽に変更をお願いします)
目次
A-PxL(旧 会津大学VR部)のこれまでの活動
2016年度
会津大VR部(会津大学) - Japan-VR-circles
3月5日:はてなブログ開設
2017年度
4月6日:会津大学内にカヤックVR分室を設立しました! | 面白法人カヤック
4月28日:Twitter開設
9月29日〜10月1日:VR部 開発合宿
10月7日〜10月8日:蒼翔祭(学祭)
12月:初代部長秋山さんが株式会社AnostVRを設立
2018年度
4月10日:新入生説明会
4月17日:今年の方針の説明 VR概論1
4月24日:VR概論2
5月8日:ゲームサウンドワークショップ
5月15日:3Dモデルワークショップ
5月22日:Git勉強会1
5月29日:Git勉強会2
6月30日:ブレスト研修会
7月3日:チーム進捗報告及びチーム活動
8月11日:オープンキャンパス
9月14日〜9月16日:開発合宿(南会津の宿泊施設CloudCamp)
10月6日〜10月7日:蒼翔祭(学祭)
10月16日:チーム進捗報告及びチーム活動
11月4日:デジゲー博2018(秋葉原UDX)
11月16日:IVRC決勝大会ユース部門銀賞を獲得
youtu.beご報告が遅くなりましたが、私たち会津大学VR部のチームFireFloworksが先日行われましたIVRC決勝大会にてユース部門銀賞を獲得することが出来ました!
— A-PxL (@aizu_PxL) 2018年11月17日
応援してくださった皆様、審査して頂いた審査員の皆様、その他多くの皆様本当にありがとうございました! https://t.co/rvEH7jDrMo
11月18日:パソコン甲子園にてゲームの展示
12月1日〜12月22日:健康づくりハッカソン
12月:部内面談
1月:部内面談
2月:次年度の役員決め
2月16日〜2月17日:新潟・山形・会津大学共同ハッカソンに参加naoto80840.github.io
2019年度
4月:役員の分担(代表者の他に副代表者、会計、自治会担当者を増員)
4月5日:部名を「Aizu-Progressive xr Lab(略称:A-PxL)」に変更
5月18日〜5月19日:VRキャラバン活動
6月16日:Oculus Quest体験会
この広い空間で近未来の機器を付けて謎の動きをする集団#A_PxL#OculusQuest体験会 pic.twitter.com/IYv9OoZzQs
— グッチー (@guchimoriVR82) 2019年6月16日
7月13日〜7月14日:新入生対抗ハッカソン
8月23日〜9月14日:学祭に向けてのPR動画制作
10月12日〜10月13日:蒼翔祭(学祭)は台風により中止
12月:部内VRコンテンツ体験会
1月31日:来年度の活動についてミーティング
2月14日:勉強会についての会議
3月1日:追いコン
2020年度
4月6日:部室があるAiCTを3Dスキャン
4月15日:clusterにてA-PxL新入生向けの説明会
4月29日〜5月10日:バーチャルマーケット4に出展
5月14日〜6月11日毎週木曜日:新入生向けのプログラミング勉強会
5月15日〜6月12日毎週金曜日:新入生向けのUnity勉強会
5月16日〜5月30日毎週土曜日:新入生向けのBlender勉強会
6月6日〜6月13日毎週土曜日:新入生向けのVFX勉強会
6月17日:OpenAppLab "2020"
7月11日:Git勉強会1
7月14日:部内でVR体験会
7月16日:Git勉強会2
7月17日:Git勉強会3
7月21日:チーム決め
9月:夏休み
10月10日:オンラインの蒼翔祭(学祭)
活動内容の変化
2016年度:?
2017年度:?
2018年度:チームに別れてゲームを制作。
2019年度:ゲーム部と新規開発部(NDD)に別れて活動。ゲーム部は毎週火曜日19:00〜21:00に中講義室M5にて定例会。新規開発部は毎週金曜日19:00〜21:00に研究棟北ラウンジにて定例会。
2020年度:ゲーム部と新規開発部を統合。毎週火曜日19:00からUBICの3Dシアターにて定例会(大学のテスト期間や大型の休みを除く)。周一でチームごとに進捗を報告しゲームを制作。
部員数の推移
(最大アクティブ数はSlackのアナリティクスより集計)
2016年度:総数11人→13人
2017年度:総数33人、最大アクティブ数33人、新部員20?人
2018年度:総数49?人、最大アクティブ数44人、新部員16人
2019年度:総数63?人、最大アクティブ数43人、新部員6人
2020年度:総数77人、最大アクティブ数38人、新部員6人
役員の推移
顧問:マイケル・コーエン先生
2017年度
代表者:Achu→Achu (@Achu_retro) | Twitter
2018年度
代表者:ゆべねこ→ゆべねこ (@yubeshineko) | Twitter
2019年度
代表者(新規開発部リーダー):Bigdra→bigdra (@bigdra50) | Twitter
副代表者(ゲーム部リーダー):なめろう→NameChung (@sketch_namerou) | Twitter
会計:ヤミみみ→ヤミみみ (@MtYamaniko) | Twitter
自治会担当者:ぶろっくのいず→ぶろっくのいず (@blocknoise26) | Twitter
2020年度前半(学祭前)
代表者:Bigdra→bigdra (@bigdra50) | Twitter
副代表者:TC→TC (@Meancore226) | Twitter
会計:ヤミみみ→ヤミみみ (@MtYamaniko) | Twitter
自治会担当者:ぶろっくのいず→ぶろっくのいず (@blocknoise26) | Twitter
2020年度後半(学祭後)
???
A-PxLメンバー達のTwitter
部室の変化
2017年度:?
2018年度:会津大学UBIC分室
2019年度:AiCT(あゆむCafe)の展示スペース
2020年度:無し
部名変更の経緯
最初は会津大学VR部という名であったA-PxL。(結局今もVR部と呼んだり呼ばれることも) 2019年3月頃、VRだけでなくARなどxRを扱う部であるのに対し、部名がそぐわないという意見が出る。(※部名の変更がちょうど年度始まりから可能) 「VRやxR関連であること」、「他と被らないオリジナリティのあること」という条件を元に30個?ほどの部名案が出る。その中から「なんちゃらxRなんちゃら(どんな名前だっけ?)」という部名がアンケートで選ばれ、最終的に「Aizu-Progressive xr Lab(略してA-PxL)」という部名になる。A-PxLの読み方は「アイヅピクセル」。
(2020年9月7日更新)
スクリプトからゲームオブジェクトのマテリアルの色を変える覚え書き
こんにちは、学部4年の杉山です。ネタが尽きたので覚え書きです。
はじめに
最近論文執筆を始めまして、その際にオブジェクトのマテリアルの色をスクリプトから変えているのでせっかくなのでメモ書き程度に紹介したいと思います。
カラーネームを使う
Unityには9(10)のカラーネームがあります。
- black
- blue
- (clear)
- cyan
- gray(grey)
- green
- magenta
- red
- white
- yellow
clearは透明であるから色として含めるかはおいておくとして、これらのカラーネームを使うなら。
GetComponent<Renderer>().material.color = Color.black
のように記述すれば好きな色に変更できます。
RGBAを使う
RGBAを使うこともできます。
Color(float r, float g, float b, float a); //それぞれ r=赤、g=緑、b=青、a=アルファ値
RGBだけでも使えます。
Color(float r, float g, float b);
例文としては
GetComponent<Renderer>().material.color = Color( 0.1f, 0.2f, 0.3f, 0.4f );
アルファ値は指定しなければ1として扱われます。
おまけ
色を変更する際に便利なメソッドがあったので紹介します
Color Lerp( Color a, Color b, float t); // t は0~1の間、0の時はa、1の時はbを返す
使うときは
GetComponent<Renderer>().material.color = Color.Lerp( Color.white, Color.black, t); // t は任意の数
最後に
わざわざスクリプトから色を変更する機会もそうそうないとは思いますが、お役に立てば幸いです。色を変えるのが億劫なときなどにどうぞ。
参考
Githubで一貫してタスク管理
皆さんこんにちは学部4年の森口です。
8月に入り急に気温が上がったことで外出自粛が促進されていてある意味感染症対策になっておりますww
さて今回はGithubの機能であるissueとprojectなどを使ってGithub内で完結したタスク管理方法についてまとめていきたいと思います。
Githubでタスク管理
Githubにおけるタスク管理で主に使う機能はissueとprojectの二つになります。これらの二つでタスクの管理をしつつPullRequestなどを組み合わせることでさらに効果的にプロジェクトの管理を進めることができます
issue
issueはGithubのタスク管理機能の一つであり、バグ報告や機能追加などのチケットを作成したり編集することができます
project
projectはいわゆるカンバン方式のタスク管理機能でTrelloに近い使用感で誰がどのタスクを行なっているのか、対応中やレビュー待ちといったタスクの進捗状況を確認することができます
無論TrelloやGoogleスプレッドシートなど他サービスを使用してのタスク管理もできますが可能な部分はGithub内で完結させることでサービスを跨いだときに起こる弊害をなくすことができより快適に開発ができると考えています。
ではここからGithubでタスク管理をしていく一連の流れと手順を説明していきます。
projectの作成
まずはprojectを新しく作成します。Githubのページ上でProjectsタブをクリックすると以下のような画面になります。
そして右上にあるCreate a ProjectをクリックするとProjectの詳細を設定する画面に移ります(ここの内容は後から変更できます)
Project templateの部分はGithub側から予め用意されているテンプレートを使用することができます。
今回はAutomated kanbanを使用していますがこれはissueを登録したり、完了した際などに自動的にカードを移動してくれます(自分で設定することで同じような物を作れますがこっちの方が楽です)
名前や概要などを記述したらCreateProjectをクリックしてProjectを作成しましょう
projectを作成するとカンバン形式の画面が表示されます
この画面でタスクの進捗状況などを確認できます。初めはTo Doにあるカードを開発を開始するときはInProgressに移動させるといった感じです。
また、かくカラムの右上にある3つの点をクリックしManage automationを選択するとどのアクションを行なったときにカードを自動的に移動させるか設定することができます。
これでprojectの設定は完了したので次に移りましょう
Milestone
issueの設定をする前にMilestoneという機能について紹介したいと思います。これはその名の如くマイルストーンを設定することができてこれをissueに紐づけることでマイルストーン毎にタスクを管理することができます。
マイルストーンの作成はIssueタブをクリックして画像内で赤く囲ったMilestonesを選択します。
そうするとマイルストーンの管理画面になるのでNewmilestoneを選択してマイルストーンを作成します。
作成画面ではタイトルと期限、詳細を設定するように言われるので記述します。
それでは今回の本題となるissueを作成していきましょう!
issueの作成
先ほどマイルストーンを作成した時と同様にIssueタブを選択し、今度はNewIssueを選択します
Issueの作成画面は以下のようになっておりここでバグ報告や新機能追加のためのissueを作成します。
GithubではMarkdownが対応しているので見出しなどを利用して概要、issue内の細々としたタスクなどを分けて記述するとよりわかりやすいと思います。
また、以下の画像でタスクリストの部分で使用している記法はGithubのタスクリストの書き方で、この記法で記述しておくとこのタスクリストの進捗状況がissueに表示されるので便利です。
詳細が書き終わったらProjectやマイルストーンを紐付けましょう。紐付けは画面右側のProjectやMilestoneの欄にある歯車を選択して該当の物を選択します。
その他の項目はAssigneesはそのチケットに関わる人、Labelはどのような種類のissueなのかを指定できます。
設定が完了したらSubmit new issueを選択してissueを作成しましょう
作成したissueには番号が割り当てられ(タイトル横の#1)この番号をコミットメッセージやプルリクエストの詳細部分に記述するとそれらからこのissueへのリンクが作成されます。
いざ開発
自分宛のIssueができたらいざ開発に移ります。まずは、自分が特定のタスクを進行中であることを他のメンバーにもわかるようにProjectのカードを移動させます
Projectを見にいくとTodoのところにカードが作成されています。これは先ほどIssueを作成したときにProjectを紐づけたので自動的に生成されました。これをドラッグアンドドロップでInProgressへ移動させましょう。
プルリクエスト
さて、開発が終わったらあとは自分の成果をマージしてもらうためプルリクエストを作成します。
プルリクエストとは自分の作業ブランチにおけるコミットをマージしてくださいと要請する機能です。個人開発では特別なものではありませんが、チーム開発ではコードレビューなどが行われるタイミングでもあり、自分のコミットに対し問題がなければある程度権限のある人が承認しマージされます。
この工程では特別することは変わらないのでプルリクエストの大まかな説明は省略し、今回のタスク管理と関係のある部分のみ説明します。
と言ってもポイントは一点だけで詳細を記述する部分でCloses (issue番号) のように記述するとそのissueはマージされた時点でCloseされます。もちろん手動でもCloseはできるので絶対描かなければならないわけではないですがそのIssueの内容が全て完了しているのであれば書いておいた方がいいでしょう。
また、プルリクエストを作成しレビューを待つ場合はProjectにRevewingのようなカラムを作成しカードを移動しておきましょう。
クローズ&リオープン
プルリクエストが承認されIssueの内容を完了したらIssueをクローズしましょう。Issueタブを選択し自分の担当していたIssueを選択したらCloseIssueを選択することでクローズすることができます。また、IssueをクローズするとProjectのカードはDoneへ移動します。
最後に
いかがでしたでしょうか。現在私の所属しているチームでタスク管理をするのに前々から気になっていたので使ってみようと思い、使い方などを調べたことを記事にしましたが非常に使い勝手が良さそうな印象を受けました。
また、今回紹介した方法は一例ですのでさらに使いやすい方法もあるかと思いますのでチームの形態によって色々とカスタマイズしてチームとして最も使いやすい形で管理を行うのが一番いいのかなと思います。それでは今回はこの辺で失礼します。