Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9749

Unity-DOTSで作る2D弾幕シューティングの例 - ④銃を操作するComponentSystem

$
0
0

銃を操作するComponentSystem

このゲームは可能な限りパフォーマンスの出る弾幕シューティングにしたかったので、
ComponentSystemは極力並列化できるJobComponentSystemを使います。

さっそくですがコードを記載します。

[UpdateAfter(typeof(EnemySystem))]publicclassGunSystem:JobComponentSystem{publicconstintGUNNER_PLAYER=1;publicconstintGUNNER_OPTION=2;publicconstintGUNNER_ENEMY=3;privateUtilSystemutilSystem;privateEntityQueryqGuns;privateEntityQueryqReading;privateEntityQueryqGunner;privateEntityQueryqBulletDefine;privateEntityQueryqColliderDefine;privateEntityQueryqColliderSetDefine;privateEntityArchetypebulletArchetype;privateCommandBufferSystembarrier;privateNativeArray<Unity.Mathematics.Random>randomArray;privateEntityManagermanager;protectedoverridevoidOnCreate(){manager=World.DefaultGameObjectInjectionWorld.EntityManager;utilSystem=World.GetExistingSystem<UtilSystem>();barrier=World.GetExistingSystem<CommandBufferSystem>();randomArray=World.GetExistingSystem<UtilSystem>().RandomArray;List<ComponentType>componentList=newList<ComponentType>{typeof(Translation),typeof(Rotation),typeof(NeedMaterialSetting),typeof(RenderSprite),typeof(Bullet),typeof(Damage),typeof(MotionData),typeof(LinearMotion),typeof(Drifting)};bulletArchetype=manager.CreateArchetype(componentList.ToArray());qGuns=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(HasParent),typeof(Gun),typeof(Translation)},});qReading=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(HasParent),typeof(Gun),typeof(Translation)},});qGunner=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(Gunner),typeof(Translation)},});qBulletDefine=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(BulletDefine)},});qColliderDefine=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(ColliderDefine)},});qColliderSetDefine=GetEntityQuery(newEntityQueryDesc(){All=newComponentType[]{typeof(ColliderSetDefine)},});}protectedoverrideJobHandleOnUpdate(JobHandleinputDeps){// 親が存在していなければ破壊するvarhasParent_array=qGuns.ToComponentDataArray<HasParent>(Allocator.TempJob);varguns_entities=qGuns.ToEntityArray(Allocator.TempJob);for(inti=0;i<hasParent_array.Length;i++){if(!manager.Exists(hasParent_array[i].entity))manager.DestroyEntity(guns_entities[i]);}hasParent_array.Dispose();guns_entities.Dispose();if(utilSystem.CanProceedFrame()&&!utilSystem.IsPause()){// 親の情報読み出しinputDeps=newJReadParentData{translation_type=GetArchetypeChunkComponentType<Translation>(false),hasParent_type=GetArchetypeChunkComponentType<HasParent>(true),gun_type=GetArchetypeChunkComponentType<Gun>(false),parentEntities_array=qGunner.ToEntityArray(Allocator.TempJob),parentTranslations_array=qGunner.ToComponentDataArray<Translation>(Allocator.TempJob),parentGunner_array=qGunner.ToComponentDataArray<Gunner>(Allocator.TempJob),}.Schedule(qReading,inputDeps);inputDeps.Complete();// 銃のパラメータ更新inputDeps=newJUpdate{gun_type=GetArchetypeChunkComponentType<Gun>(false),}.Schedule(qGuns,inputDeps);// 弾を撃つinputDeps=newJShot{translation_type=GetArchetypeChunkComponentType<Translation>(true),gun_type=GetArchetypeChunkComponentType<Gun>(false),bulletArchetype=bulletArchetype,colliderArchetype=SettingManager.Get().ColliderArchetype,commandBuffer=barrier.CreateCommandBuffer().ToConcurrent(),random_array=randomArray,bulletDefine_array=qBulletDefine.ToComponentDataArray<BulletDefine>(Allocator.TempJob),colliderDefine_array=qColliderDefine.ToComponentDataArray<ColliderDefine>(Allocator.TempJob),colliderSetDefine_array=qColliderSetDefine.ToComponentDataArray<ColliderSetDefine>(Allocator.TempJob),}.Schedule(qGuns,inputDeps);}// Job内で生成コマンドを実行するため、Job完了させるinputDeps.Complete();returninputDeps;}[BurstCompile]privatestructJReadParentData:IJobChunk{publicArchetypeChunkComponentType<Translation>translation_type;publicArchetypeChunkComponentType<Gun>gun_type;[ReadOnly]publicArchetypeChunkComponentType<HasParent>hasParent_type;[ReadOnly][DeallocateOnJobCompletion]publicNativeArray<Entity>parentEntities_array;[ReadOnly][DeallocateOnJobCompletion]publicNativeArray<Translation>parentTranslations_array;[ReadOnly][DeallocateOnJobCompletion]publicNativeArray<Gunner>parentGunner_array;publicvoidExecute(ArchetypeChunkchunk,intchunkIndex,intfirstEntityIndex){vartranslation_array=chunk.GetNativeArray(translation_type);varhasParent_array=chunk.GetNativeArray(hasParent_type);vargun_array=chunk.GetNativeArray(gun_type);varc=translation_array.Length;for(inti=0;i<c;i++){Translationposition=translation_array[i];HasParenthasParent=hasParent_array[i];Gungun=gun_array[i];for(intj=0;j<parentEntities_array.Length;j++){if(parentEntities_array[j]==hasParent.entity){// 親の位置を自身に反映し、書き戻すTranslationparentPosition=parentTranslations_array[j];GunnerparentGunner=parentGunner_array[j];gun.isTriggered=parentGunner.isTriggered;gun.shotAngle=parentGunner.angle;gun.isNoInterval=parentGunner.isNoInterval;position.Value.x=parentPosition.Value.x+hasParent.xOffset;position.Value.y=parentPosition.Value.y+hasParent.yOffset;translation_array[i]=position;gun_array[i]=gun;}}}}}[BurstCompile]privatestructJUpdate:IJobChunk{publicArchetypeChunkComponentType<Gun>gun_type;publicvoidExecute(ArchetypeChunkchunk,intchunkIndex,intfirstEntityIndex){vargun_array=chunk.GetNativeArray(gun_type);for(inti=0;i<gun_array.Length;i++){vargun=gun_array[i];// 待機カウンタを減算if(!gun.isTriggered)continue;if(gun.isNoInterval)gun.counter=0;gun.counter--;// 待機カウンタ満了if(gun.counter<0){// マガジンに弾がある(連射中)if(gun.magazine>0){// 連射数と現在の射出数からイーズ用の進行度を割り出す// 連射数 - マガジンの残数 = 射撃した数// 射撃した数 / 連射数 = 進行度floatprogress=(float)(gun.shotBlazeCount-gun.magazine)/gun.shotBlazeCount;floatease=Easing.Get(gun.shotAngularEaseType,progress);if(gun.isEaseReversing)ease=-ease;gun.direction+=gun.shotAngularSpeed*ease;gun.bulletSpeed+=gun.bulletAcceralation;}// マガジンに弾がない(連射開始)else{gun.magazine=gun.shotBlazeCount;gun.direction=gun.shotAngle;gun.bulletSpeed=gun.bulletSpeedDefault;// 角速度反転if(gun.shotAngluarReverse)gun.isEaseReversing=!gun.isEaseReversing;}}// 値の書き戻しgun_array[i]=gun;}}}[BurstCompile]privatestructJShot:IJobChunk{publicNativeArray<Unity.Mathematics.Random>random_array;publicEntityCommandBuffer.ConcurrentcommandBuffer;publicArchetypeChunkComponentType<Gun>gun_type;[ReadOnly]publicArchetypeChunkComponentType<Translation>translation_type;[DeallocateOnJobCompletion]publicNativeArray<BulletDefine>bulletDefine_array;[DeallocateOnJobCompletion]publicNativeArray<ColliderDefine>colliderDefine_array;[DeallocateOnJobCompletion]publicNativeArray<ColliderSetDefine>colliderSetDefine_array;publicEntityArchetypebulletArchetype;publicEntityArchetypecolliderArchetype;publicvoidExecute(ArchetypeChunkchunk,intchunkIndex,intfirstEntityIndex){vargun_array=chunk.GetNativeArray(gun_type);vartranslation_array=chunk.GetNativeArray(translation_type);varrandom=random_array[chunkIndex];for(inti=0;i<gun_array.Length;i++){vartranslation=translation_array[i];vargun=gun_array[i];// カウンタが継続中の場合は何もしないif(gun.counter>=0)continue;// マガジンに弾がある(連射中)if(gun.magazine>0){// 偶数Wayショットか否かboolisEvenWay=(gun.shotWays%2==0);boolpairedWay=false;floatwayInterval=0f;// 射撃Way数だけループするfor(intj=0;j<gun.shotWays;j++){floatwayDir=gun.direction;if(j==0){// 偶数Wayの場合は発射せず、射出方向角度の半分を加えるif(isEvenWay){wayInterval+=gun.shotWaysAngleInterval/2;wayDir+=wayInterval;pairedWay=true;}else{wayInterval+=gun.shotWaysAngleInterval;}}else{if(pairedWay){wayDir-=wayInterval;wayInterval+=gun.shotWaysAngleInterval;}else{wayDir+=wayInterval;}pairedWay=!pairedWay;}// 同時射撃数回数だけループするboolisEvenShot=(gun.shotSimultaneousCount%2==0);boolpairedShot=false;floatbulletAngleInterval=0f;floatbulletPlaceInterval=0f;for(intk=0;k<gun.shotSimultaneousCount;k++){floatmoveDirection=wayDir;floatplaceDirection=wayDir;floatinitialSpeed=gun.bulletSpeed;if(k==0){// 偶数Wayの場合は発射せず、射出方向角度の半分を加えるif(isEvenShot){bulletAngleInterval+=gun.shotBulletAngleInterval/2;bulletPlaceInterval+=gun.shotBulletPlaceInterval/2;moveDirection+=bulletAngleInterval;placeDirection+=bulletPlaceInterval;pairedShot=true;}else{bulletAngleInterval+=gun.shotBulletAngleInterval;bulletPlaceInterval+=gun.shotBulletPlaceInterval;}}else{if(pairedShot){moveDirection-=bulletAngleInterval;placeDirection-=bulletPlaceInterval;bulletAngleInterval+=gun.shotBulletAngleInterval;bulletPlaceInterval+=gun.shotBulletPlaceInterval;}else{moveDirection+=bulletAngleInterval;placeDirection+=bulletPlaceInterval;}pairedShot=!pairedShot;}// 角度にランダム係数を加えるif(gun.shotAngularRandomize>0){moveDirection+=random.NextFloat(-gun.shotAngularRandomize,gun.shotAngularRandomize);random_array[chunkIndex]=random;// ランダム配列に書き戻しが必要}// 射出速度にランダム係数を加えるif(gun.bulletSpeedRandomize>0){initialSpeed+=random.NextFloat(0,gun.bulletSpeedRandomize);random_array[chunkIndex]=random;// ランダム配列に書き戻しが必要}// 弾定義データからIDに合致するものを拾い、弾エンティティの生成for(intbltInd=0;bltInd<bulletDefine_array.Length;bltInd++){if(gun.bulletId==bulletDefine_array[bltInd].BulletId){BulletDefinebulletDefine=bulletDefine_array[bltInd];EntitybulletEntity=commandBuffer.CreateEntity(chunkIndex,bulletArchetype);floatmoveRad=moveDirection*math.PI*2;float3moveDirectionVec=newfloat3(math.cos(moveRad),math.sin(moveRad),0);floatplaceRad=placeDirection*math.PI*2;float3placeDirectionVec=newfloat3(math.cos(placeRad)*gun.shotOffsetRadius,math.sin(placeRad)*gun.shotOffsetRadius,0);float3accelerationVec=newfloat3(bulletDefine.AccerelationX,bulletDefine.AccerelationY,0);commandBuffer.SetComponent(chunkIndex,bulletEntity,newRotation{radian=moveRad});commandBuffer.SetComponent(chunkIndex,bulletEntity,newTranslation{Value=translation.Value+placeDirectionVec});commandBuffer.SetComponent(chunkIndex,bulletEntity,newLinearMotion{Activated=true,direction=moveDirectionVec,acceleration=accelerationVec,});commandBuffer.SetComponent(chunkIndex,bulletEntity,newMotionData{Velocity=math.normalize(moveDirectionVec)*initialSpeed,friction=math.normalize(newfloat3(1,1,0)),});commandBuffer.SetComponent(chunkIndex,bulletEntity,newRenderSprite{materialId=bulletDefine.MoveAnimationId,speed=10,scaleCurrent=newfloat2(1,1),sortingOrder=SpriteRenderSystem.SORTING_BULLET,});commandBuffer.SetComponent(chunkIndex,bulletEntity,newNeedMaterialSetting{Is=true,});commandBuffer.SetComponent(chunkIndex,bulletEntity,newDamage{value=gun.damage,});// コライダー作成for(intsetInd=0;setInd<colliderSetDefine_array.Length;setInd++){if(bulletDefine.ColliderSetId==colliderSetDefine_array[setInd].colliderSetId){vartarget=colliderSetDefine_array[setInd].colliderId;varxOffset=colliderSetDefine_array[setInd].xOffset;varyOffset=colliderSetDefine_array[setInd].yOffset;for(intcolInd=0;colInd<colliderDefine_array.Length;colInd++){if(target==colliderDefine_array[colInd].colliderId){vardata=colliderDefine_array[colInd];EntitycolliderEntity=commandBuffer.CreateEntity(chunkIndex,colliderArchetype);varbufferId=random.NextInt(0,HitCheckSystem.HIT_CHECK_BUFFER_COUNT);random_array[chunkIndex]=random;// ランダム配列に書き戻しが必要commandBuffer.SetComponent(chunkIndex,colliderEntity,newCollider{IsDestructive=bulletDefine.IsDestructive,kind=gun.colliderKind,width=data.width,height=data.height,range=data.range,radius=data.radius,shape=data.shape,IsBufferedCheck=true,hitCheckBufferId=bufferId,IsActive=true,});commandBuffer.SetComponent(chunkIndex,colliderEntity,newTranslation{Value=newfloat3(0,0,0),});commandBuffer.SetComponent(chunkIndex,colliderEntity,newHasParent{entity=bulletEntity,xOffset=xOffset,yOffset=yOffset,});break;}}}}break;}}}}gun.magazine--;// 残弾数があれば連射間隔をカウンタに設定// 残弾数が0になっていたら停止間隔をカウンタに設定if(gun.magazine>0){gun.counter=gun.shotBlazeInterval;}else{gun.counter=gun.shotTimeInterval;}}gun_array[i]=gun;}}}}

Jobを記述するstructには頭文字Jをつけるようにしています。

OnCreateでは、Entityの種別ごとに利用するクエリを組み立ててキャッシュしておきます。
OnUpdateでは、銃Entityの処理を実行する前に、親として設定されているEntityがまだ存在しているかをチェックして、いなければそのEntityは破壊します。
そのあと、本体となる下記三つのJobを順番に実行していきます。
JReadParentDataは、親となるEntityの情報を銃が拾うためのものです。主に親の座標を拾って自分の位置を更新するのに使います。その際、親の向いている角度も拾っておき、射撃角度のベースとして使用します。

このゲームでは、銃が射撃を開始する場合、設定されている連射数の数だけマガジンに弾をセットします。それを連射間隔に応じて撃っていき、マガジンの残数が0になったら設定されている待機時間になるまで待機カウンタを回します。
JUpdateJShotでは、銃が射撃可能な状態かどうかをチェックし、可能であれば弾Entityを作成します。
複数Wayの射撃を行うため、射撃の際は射撃Wayの数だけループを行います。その内部で、Wayごとに複数の弾を撃つ場合は、さらにループを回します。例えば、3Wayの射撃で、三つの方向に角度が若干ランダム化された弾を五つずつ撃ちたいというような場合に使えます。

コードが長い割にやっていること自体は単純かもしれません。


Viewing all articles
Browse latest Browse all 9749

Trending Articles