DEG-55 [FEAT] 플레이어 피격 이펙트 구현
This commit is contained in:
parent
2ebeccfa69
commit
e39b91600e
13
.idea/.idea.Degulleo/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.Degulleo/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# 디폴트 무시된 파일
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider에서 무시된 파일
|
||||
/.idea.Degulleo.iml
|
||||
/contentModel.xml
|
||||
/modules.xml
|
||||
/projectSettingsUpdater.xml
|
||||
# 에디터 기반 HTTP 클라이언트 요청
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
4
.idea/.idea.Degulleo/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.Degulleo/.idea/encodings.xml
generated
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
8
.idea/.idea.Degulleo/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.Degulleo/.idea/indexLayout.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
6
.idea/.idea.Degulleo/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.Degulleo/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
BIN
Assets/JAY/Animation/GetHit.anim
(Stored with Git LFS)
Normal file
BIN
Assets/JAY/Animation/GetHit.anim
(Stored with Git LFS)
Normal file
Binary file not shown.
8
Assets/JAY/Animation/GetHit.anim.meta
Normal file
8
Assets/JAY/Animation/GetHit.anim.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5e300b40f91e7746956016cd1a33da6
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/JAY/Animator/PlayerController.controller
(Stored with Git LFS)
BIN
Assets/JAY/Animator/PlayerController.controller
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/JAY/Character Test Scene.unity
(Stored with Git LFS)
BIN
Assets/JAY/Character Test Scene.unity
(Stored with Git LFS)
Binary file not shown.
@ -1,6 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dfbb63c9cdf7504faf4ff26b0581598
|
||||
PrefabImporter:
|
||||
guid: 264ff5a75e22d3f489e174962ff5f899
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
BIN
Assets/JAY/Image/red_splatter.png
(Stored with Git LFS)
Normal file
BIN
Assets/JAY/Image/red_splatter.png
(Stored with Git LFS)
Normal file
Binary file not shown.
127
Assets/JAY/Image/red_splatter.png.meta
Normal file
127
Assets/JAY/Image/red_splatter.png.meta
Normal file
@ -0,0 +1,127 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1020bfc1c110a2e47a7f4eda301bd3db
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
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: 0
|
||||
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: 8
|
||||
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: 1
|
||||
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:
|
3
Assets/JAY/Scripts/Effect.meta
Normal file
3
Assets/JAY/Scripts/Effect.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f85f06ffdab841aba19658d505e1a75a
|
||||
timeCreated: 1745899456
|
41
Assets/JAY/Scripts/Effect/CameraShake.cs
Normal file
41
Assets/JAY/Scripts/Effect/CameraShake.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class CameraShake : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float shakeDuration = 0.2f;
|
||||
[SerializeField] private float shakeMagnitude = 0.1f;
|
||||
|
||||
private Vector3 initialLocalPosition;
|
||||
private Coroutine shakeCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
initialLocalPosition = transform.localPosition;
|
||||
}
|
||||
|
||||
public void Shake()
|
||||
{
|
||||
if (shakeCoroutine != null)
|
||||
{
|
||||
StopCoroutine(shakeCoroutine);
|
||||
}
|
||||
shakeCoroutine = StartCoroutine(ShakeRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator ShakeRoutine()
|
||||
{
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < shakeDuration)
|
||||
{
|
||||
Vector3 randomPoint = Random.insideUnitSphere * shakeMagnitude;
|
||||
transform.localPosition = initialLocalPosition + randomPoint;
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
transform.localPosition = initialLocalPosition;
|
||||
}
|
||||
}
|
3
Assets/JAY/Scripts/Effect/CameraShake.cs.meta
Normal file
3
Assets/JAY/Scripts/Effect/CameraShake.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84537ad9e9c54885bdc75a1cd0e7a1df
|
||||
timeCreated: 1745902439
|
48
Assets/JAY/Scripts/Effect/PlayerHitEffectController.cs
Normal file
48
Assets/JAY/Scripts/Effect/PlayerHitEffectController.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
|
||||
public class PlayerHitEffectController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image hitFlashImage;
|
||||
private float flashDuration = 0.7f;
|
||||
private Color flashColor = new Color(1, 0, 0, 0.7f); // 반투명 빨간색
|
||||
|
||||
private Coroutine flashCoroutine;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
hitFlashImage.color = Color.clear; // 처음에는 투명함
|
||||
}
|
||||
|
||||
public void PlayHitEffect()
|
||||
{
|
||||
if (flashCoroutine != null)
|
||||
{
|
||||
StopCoroutine(flashCoroutine);
|
||||
}
|
||||
flashCoroutine = StartCoroutine(FlashRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator FlashRoutine()
|
||||
{
|
||||
// 일단 바로 최대 알파로 세팅
|
||||
hitFlashImage.color = flashColor;
|
||||
|
||||
float timer = 0f;
|
||||
while (timer < flashDuration)
|
||||
{
|
||||
timer += Time.deltaTime;
|
||||
|
||||
// 알파만 줄이기
|
||||
Color c = flashColor;
|
||||
c.a = Mathf.Lerp(flashColor.a, 0, timer / flashDuration);
|
||||
hitFlashImage.color = c;
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
hitFlashImage.color = Color.clear;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d7446d96b0c4470b42ee010cbbd809f
|
||||
timeCreated: 1745899493
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.TextCore.Text;
|
||||
using UnityEngine;
|
||||
|
||||
public enum PlayerState { None, Idle, Move, Win, Hit, Dead }
|
||||
@ -10,8 +11,10 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
// 외부 접근 가능 변수
|
||||
[Header("Attach Points")]
|
||||
[SerializeField] private Transform rightHandTransform;
|
||||
|
||||
[SerializeField] private CameraShake cameraShake;
|
||||
|
||||
// 내부에서만 사용하는 변수
|
||||
private PlayerHitEffectController hitEffectController;
|
||||
private CharacterController _characterController;
|
||||
private bool _isBattle;
|
||||
private GameObject weapon;
|
||||
@ -52,25 +55,9 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
{
|
||||
base.Start();
|
||||
|
||||
// 상태 초기화
|
||||
_playerStateIdle = new PlayerStateIdle();
|
||||
_playerStateMove = new PlayerStateMove();
|
||||
_playerStateWin = new PlayerStateWin();
|
||||
_playerStateDead = new PlayerStateDead();
|
||||
|
||||
_playerStates = new Dictionary<PlayerState, IPlayerState>
|
||||
{
|
||||
{ PlayerState.Idle, _playerStateIdle },
|
||||
{ PlayerState.Move, _playerStateMove },
|
||||
{ PlayerState.Win, _playerStateWin },
|
||||
{ PlayerState.Dead, _playerStateDead },
|
||||
};
|
||||
|
||||
_attackAction = new PlayerActionAttack();
|
||||
_actionDash = new PlayerActionDash();
|
||||
hitEffectController = GetComponentInChildren<PlayerHitEffectController>();
|
||||
|
||||
PlayerInit();
|
||||
|
||||
SwitchBattleMode();
|
||||
}
|
||||
|
||||
@ -91,7 +78,6 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
// 공격 입력 처리
|
||||
if (Input.GetKeyDown(KeyCode.X) && (_currentAction == null || !_currentAction.IsActive)
|
||||
&& (CurrentState != PlayerState.Win && CurrentState != PlayerState.Dead)) {
|
||||
Debug.Log("X 버튼 Down 됨");
|
||||
StartAttackAction();
|
||||
}
|
||||
|
||||
@ -101,11 +87,36 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
_currentAction.UpdateAction();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
OnGetHit -= TakeDamage;
|
||||
}
|
||||
|
||||
#region 초기화 관련
|
||||
|
||||
private void PlayerInit()
|
||||
{
|
||||
// 상태 초기화
|
||||
_playerStateIdle = new PlayerStateIdle();
|
||||
_playerStateMove = new PlayerStateMove();
|
||||
_playerStateWin = new PlayerStateWin();
|
||||
_playerStateDead = new PlayerStateDead();
|
||||
|
||||
_playerStates = new Dictionary<PlayerState, IPlayerState>
|
||||
{
|
||||
{ PlayerState.Idle, _playerStateIdle },
|
||||
{ PlayerState.Move, _playerStateMove },
|
||||
{ PlayerState.Win, _playerStateWin },
|
||||
{ PlayerState.Dead, _playerStateDead },
|
||||
};
|
||||
|
||||
_attackAction = new PlayerActionAttack();
|
||||
_actionDash = new PlayerActionDash();
|
||||
|
||||
OnGetHit -= TakeDamage;
|
||||
OnGetHit += TakeDamage;
|
||||
|
||||
SetState(PlayerState.Idle);
|
||||
|
||||
InstantiateWeapon();
|
||||
@ -182,7 +193,6 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
if (_weaponController.IsAttacking) return; // 이미 공격 중이면 실행 안함
|
||||
|
||||
if (_currentAction == _attackAction) {
|
||||
Debug.Log($"Attack True");
|
||||
_attackAction.EnableCombo();
|
||||
_weaponController.AttackStart();
|
||||
}
|
||||
@ -191,7 +201,6 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
public void SetAttackComboFalse()
|
||||
{
|
||||
if (_currentAction == _attackAction) {
|
||||
Debug.Log($"Attack False");
|
||||
// 이벤트 중복 호출? 공격 종료 시 SetAttackComboFalse가 아니라 ~True로 끝나서 오류 발생. (공격 안하는 상태여도 공격으로 판정됨)
|
||||
_attackAction.DisableCombo();
|
||||
_weaponController.AttackEnd(); // IsAttacking = false로 변경
|
||||
@ -245,4 +254,40 @@ public class PlayerController : CharacterBase, IObserver<GameObject>
|
||||
|
||||
#endregion
|
||||
|
||||
#region 회피 관련
|
||||
|
||||
// TODO: Editor에서 확인하기 위한 임시용
|
||||
public void TakeDamage()
|
||||
{
|
||||
if (CurrentState == PlayerState.Dead) return;
|
||||
|
||||
// 피격 이벤트 재생
|
||||
PlayerHitEffect();
|
||||
|
||||
// 죽었는지 체크
|
||||
if (currentHP <= 0) SetState(PlayerState.Dead);
|
||||
}
|
||||
|
||||
|
||||
public void TakeDamage(CharacterBase character)
|
||||
{
|
||||
if (character != this) return; // 혹시 다른 애가 맞은 경우 무시
|
||||
if (CurrentState == PlayerState.Dead) return;
|
||||
|
||||
// 피격 이벤트 재생
|
||||
PlayerHitEffect();
|
||||
|
||||
// 죽었는지 체크
|
||||
if (currentHP <= 0) SetState(PlayerState.Dead);
|
||||
}
|
||||
|
||||
private void PlayerHitEffect()
|
||||
{
|
||||
if (_currentAction != _attackAction || !_attackAction.IsActive)
|
||||
PlayerAnimator.SetTrigger("GetHit");
|
||||
hitEffectController.PlayHitEffect();
|
||||
cameraShake.Shake();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ public class PlayerControllerEditor : Editor
|
||||
playerController.SwitchBattleMode();
|
||||
if (GUILayout.Button("Win"))
|
||||
playerController.SetState(PlayerState.Win);
|
||||
// if (GUILayout.Button("Hit"))
|
||||
// playerController.SetState(PlayerState.Hit);
|
||||
if (GUILayout.Button("Hit"))
|
||||
playerController.TakeDamage();
|
||||
if (GUILayout.Button("Dead"))
|
||||
playerController.SetState(PlayerState.Dead);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -936,11 +936,11 @@ PlayerSettings:
|
||||
apiCompatibilityLevel: 6
|
||||
activeInputHandler: 0
|
||||
windowsGamepadBackendHint: 0
|
||||
cloudProjectId: d6cf79ae-9af7-48a0-9630-2e601e15eb4b
|
||||
cloudProjectId:
|
||||
framebufferDepthMemorylessMode: 0
|
||||
qualitySettingsNames: []
|
||||
projectName: Degulleo3D 2025-04-16_10-27-08
|
||||
organizationId: aaniot
|
||||
projectName:
|
||||
organizationId:
|
||||
cloudEnabled: 0
|
||||
legacyClampBlendShapeWeights: 0
|
||||
hmiLoadingImage: {fileID: 0}
|
||||
|
Loading…
x
Reference in New Issue
Block a user