DEG-28 DEG-33 [feat] 몬스터 공격 패턴 스크립트 생성

- 직선, 부채꼴, 원형, 도넛형 선언
- 원형 AOE 인디케이터 생성
This commit is contained in:
fiore 2025-04-21 00:58:10 +09:00
parent 01acdb70c7
commit a40149c626
17 changed files with 322 additions and 14 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:456cfb85b82895096e12d09ac206e0afdd9d487c2cce78d73c84693a4883e337
size 21552
oid sha256:b9e37c8d0a91f3599d34a7715c632cbcf76fd3c0a96c5d5335ab9d6ff65d3b4c
size 18916

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e841b33c73e8fd44a8cf0bc9c1ae488
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,75 @@
Shader "Unlit/AOE_ShaderGraph"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
_Radius ("Radius", Range(0,1)) = 0.5
_Feather ("Feather", Range(0,1)) = 0.1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Color;
float _Radius;
float _Feather;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 중심에서의 거리 계산
float2 centeredUV = i.uv - 0.5;
float dist = length(centeredUV);
// 알파 조절: 중심에서 점점 투명 → 가장자리는 불투명
float alpha = smoothstep(_Radius, _Radius - _Feather, dist);
// 텍스처 적용 (선택)
fixed4 texCol = tex2D(_MainTex, i.uv);
// 최종 컬러 (컬러 * 텍스처 * 알파)
fixed4 col = _Color * texCol;
col.a *= alpha;
return col;
}
ENDCG
}
}
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 048956df0d9d82445bcfc730131181dd
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2f68ca65948709a37c1781fb40ff6ddfe393e145fb5704467abd99e26a297931
size 978

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 478a013fb343575418a252ed84af0c58
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/JYY/Prefabs/AOEIndicator.prefab (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e9e020ef2784edf4ca2a83ae9e1edefd
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/JYY/Scenes/MonsterTest.unity (Stored with Git LFS)

Binary file not shown.

8
Assets/JYY/Sprites.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d7dcd0a2efc35e04287877979738ea37
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Assets/JYY/Sprites/AOEIndicator.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: fab9fda9e2e50b1458af69d2b01d167c
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using UnityEngine;
public class EnemyAttackController : MonoBehaviour
{
[Header("각종 전조 오브젝트")]
[SerializeField] private GameObject verticalWarningArea;
[SerializeField] private GameObject horizontalWarningArea;
[SerializeField] private GameObject chariotWarningArea;
[SerializeField] private GameObject dynamoWarningArea;
// 배열에 담아서 관리
private List<GameObject> warningAreas;
private GameObject _activeArea;
private void Awake()
{
// Awake 시점에 리스트로 묶어두면 이후 유지보수 편리
warningAreas = new List<GameObject>()
{
verticalWarningArea,
horizontalWarningArea,
chariotWarningArea,
dynamoWarningArea
};
}
// 랜덤 전조 호출
// 랜덤 전조 호출
public void TriggerRandomWarning(Vector3 spawnPosition, Quaternion spawnRotation)
{
// 0 ~ Count-1 사이 랜덤 인덱스
int idx = Random.Range(0, warningAreas.Count);
GameObject selected = warningAreas[idx];
// 예시: Instantiate 방식으로 화면에 띄우기
_activeArea = Instantiate(selected, spawnPosition, spawnRotation);
}
public void DestroyWarningArea()
{
Destroy(_activeArea);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6423b2bcccf1448ca438a8e82a79a4cb
timeCreated: 1745149371

View File

@ -6,6 +6,7 @@ public enum EnemyState { None, Idle, Trace, Attack, GetHit, Move, Dead }
[RequireComponent(typeof(NavMeshAgent))]
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(EnemyAttackController))]
public abstract class EnemyController : CharacterBase
{
[Header("AI")]
@ -17,6 +18,8 @@ public abstract class EnemyController : CharacterBase
public EnemyState CurrentState {get; private set;}
public EnemyAttackController EnemyAttackController { get; private set; }
public float WalkSpeed => walkSpeed;
public float RunSpeed => runSpeed;
@ -41,6 +44,7 @@ public abstract class EnemyController : CharacterBase
{
EnemyAnimator = GetComponent<Animator>();
Agent = GetComponent<NavMeshAgent>();
EnemyAttackController = GetComponent<EnemyAttackController>();
}
protected override void Start()

View File

@ -10,7 +10,7 @@ public class EnemyStateAttack : IEnemyState
private EnemyController _enemyController;
private Animator _animator;
private Coroutine _attackRoutine;
private EnemyAttackController _enemyAttackController;
private enum AttackType
{
VerticalAttack, // 위에서 아래로 베는 것
@ -25,7 +25,7 @@ public class EnemyStateAttack : IEnemyState
{
_enemyController = enemyController;
_animator = _enemyController.EnemyAnimator;
_enemyAttackController = _enemyController.EnemyAttackController;
_animator.SetBool(VertiAttack, true);
_attackRoutine = _enemyController.StartCoroutine(VerticalAttackSequence());
}
@ -37,21 +37,19 @@ public class EnemyStateAttack : IEnemyState
private IEnumerator VerticalAttackSequence()
{
// 1. 전조 이펙트 생성
_enemyAttackController.TriggerRandomWarning(_enemyController.transform.position, _enemyController.transform.rotation);
// 2. 검을 들어올림
yield return new WaitForSeconds(3f);
// 3. 대기(전조와 검 들어올리는 애니메이션을 위함)
// 4. 전조 제거
// 5. 검 휘두르기
// 4. 검 휘두르기
_animator.SetTrigger(VertiSlash);
// 6. 공격 판정 발생
_enemyAttackController.DestroyWarningArea();
// TODO : 5. 공격 판정 발생
yield return new WaitForSeconds(1f);
// 7. 애니메이션 트리거 종료 -> 애니메이터 상태 머신으로 처리
// 6. 애니메이션 트리거 종료 -> 애니메이터 상태 머신으로 처리
_enemyController.SetState(EnemyState.Trace);
}
@ -64,6 +62,7 @@ public class EnemyStateAttack : IEnemyState
}
_animator.SetBool(VertiAttack, false);
_animator = null;
_enemyAttackController = null;
_enemyController = null;
}
}

View File

@ -50,7 +50,14 @@ public class EnemyStateTrace : IEnemyState
{
if (_detectPlayerInCircleWaitTime > MaxDetectPlayerInCircleWaitTime)
{
_enemyController.Agent.SetDestination(_detectPlayerTransform.position);
// 방향 계산
Vector3 dirToPlayer = (_detectPlayerTransform.position - _enemyController.transform.position).normalized;
// 플레이어 방향으로 offset 적용 (예: 1.5m 앞)
Vector3 stopOffset = _detectPlayerTransform.position - dirToPlayer * 1.5f;
// 목적지 설정
_enemyController.Agent.SetDestination(stopOffset);
_detectPlayerInCircleWaitTime = 0f;
}