ADD : 돈주머니 프랍 추가 및 카메라 전환 버그 해결 및 fov 제어기능 추가
This commit is contained in:
parent
ba817cb4e3
commit
6a7deb6e96
8
Assets/Preset.meta
Normal file
8
Assets/Preset.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecda75af11211dd4ead9d2948b3d3b7e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Preset/후원.meta
Normal file
8
Assets/Preset/후원.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d80c97dc0290914c9ccf17efc450e2f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Preset/후원/WefLab.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/Preset/후원/WefLab.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
7
Assets/Preset/후원/WefLab.prefab.meta
Normal file
7
Assets/Preset/후원/WefLab.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9470035ddd1c1aa4bb67805985097d7d
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Preset/후원/던질 프랍.meta
Normal file
8
Assets/Preset/후원/던질 프랍.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7066e932cb18c694594dc51b487741c2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Preset/후원/던질 프랍/돈주머니.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/Preset/후원/던질 프랍/돈주머니.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
7
Assets/Preset/후원/던질 프랍/돈주머니.prefab.meta
Normal file
7
Assets/Preset/후원/던질 프랍/돈주머니.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b5a0e552e06dfd4a82dfbaa0d8fdac3
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/Resources/Settings/BackgroundSceneDatabase.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings/BackgroundSceneDatabase.asset
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/Resources/Settings/PropDatabase.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings/PropDatabase.asset
(Stored with Git LFS)
Binary file not shown.
BIN
Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings/Streamingle Render Pipeline Asset_Renderer.asset
(Stored with Git LFS)
Binary file not shown.
8
Assets/ResourcesData/Prop/돈주머니.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b7d1ad4cc4839d408d3d6e39ec6047f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Model.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Model.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06d22216356ccad42b5434d79bc5ac8c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Materials.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Materials.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2195070c29d0ed04ab72ed53f6780ce0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1191
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Materials/material_0.mat
Normal file
1191
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Materials/material_0.mat
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83c9cb0f42010264f86ac30b1da1432d
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Textures.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Textures.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60e1891c4dc0c374da17786077d08f80
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Textures/Image_0.png
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.Textures/Image_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,117 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ded3cb3c3ae6f243b3a449aef14524f
|
||||
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: 2
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
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: 0
|
||||
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: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 512
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
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
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.glb
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.glb
(Stored with Git LFS)
Normal file
Binary file not shown.
22
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.glb.meta
Normal file
22
Assets/ResourcesData/Prop/돈주머니/Model/돈주머니.glb.meta
Normal file
@ -0,0 +1,22 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61efca492f301314c9b7189c17f6e417
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects:
|
||||
- first:
|
||||
type: UnityEngine:Material
|
||||
assembly: UnityEngine.CoreModule
|
||||
name: material_0
|
||||
second: {fileID: 2100000, guid: 83c9cb0f42010264f86ac30b1da1432d, type: 2}
|
||||
- first:
|
||||
type: UnityEngine:Texture
|
||||
assembly: UnityEngine.CoreModule
|
||||
name: Image_0
|
||||
second: {fileID: 2800000, guid: 3ded3cb3c3ae6f243b3a449aef14524f, type: 3}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 11500000, guid: cc45016b844e7624dae3aec10fb443ea, type: 3}
|
||||
reverseAxis: 2
|
||||
renderPipeline: 1
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Particle.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Particle.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 259f604cdbf223b4c85a423dd5d3d63c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Particle/Coin Particle System.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Particle/Coin Particle System.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80d0fe3b10048864f93050efd36883e9
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
158
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.mat
Normal file
158
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.mat
Normal file
@ -0,0 +1,158 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Coin
|
||||
m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords:
|
||||
- _ALPHATEST_ON
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 0
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: 2450
|
||||
stringTagMap:
|
||||
RenderType: TransparentCutout
|
||||
disabledShaderPasses:
|
||||
- MOTIONVECTORS
|
||||
- GRABPASS
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BaseMap:
|
||||
m_Texture: {fileID: 2800000, guid: 08c6cf6250054064fb85c4f953406a6f, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: 08c6cf6250054064fb85c4f953406a6f, type: 3}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _SpecGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_Lightmaps:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_LightmapsInd:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- unity_ShadowMasks:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _AddPrecomputedVelocity: 0
|
||||
- _AlphaClip: 0
|
||||
- _AlphaToMask: 0
|
||||
- _Blend: 0
|
||||
- _BlendModePreserveSpecular: 1
|
||||
- _BlendOp: 0
|
||||
- _BumpScale: 1
|
||||
- _CameraFadingEnabled: 0
|
||||
- _CameraFarFadeDistance: 2
|
||||
- _CameraNearFadeDistance: 1
|
||||
- _ClearCoatMask: 0
|
||||
- _ClearCoatSmoothness: 0
|
||||
- _ColorMode: 0
|
||||
- _Cull: 0
|
||||
- _Cutoff: 0.5
|
||||
- _DetailAlbedoMapScale: 1
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DistortionBlend: 0.5
|
||||
- _DistortionEnabled: 0
|
||||
- _DistortionStrength: 1
|
||||
- _DistortionStrengthScaled: 0
|
||||
- _DstBlend: 0
|
||||
- _DstBlendAlpha: 0
|
||||
- _EmissionEnabled: 0
|
||||
- _EnvironmentReflections: 1
|
||||
- _FlipbookMode: 0
|
||||
- _GlossMapScale: 0
|
||||
- _Glossiness: 0
|
||||
- _GlossyReflections: 0
|
||||
- _LightingEnabled: 0
|
||||
- _Metallic: 0
|
||||
- _Mode: 1
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.005
|
||||
- _QueueOffset: 0
|
||||
- _ReceiveShadows: 1
|
||||
- _Smoothness: 0.5
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SoftParticlesEnabled: 0
|
||||
- _SoftParticlesFarFadeDistance: 1
|
||||
- _SoftParticlesNearFadeDistance: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _SrcBlendAlpha: 1
|
||||
- _Surface: 0
|
||||
- _WorkflowMode: 1
|
||||
- _XRMotionVectorsPass: 1
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _CameraFadeParams: {r: 0, g: Infinity, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _ColorAddSubDiff: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _SoftParticleFadeParams: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
--- !u!114 &6738923856525603992
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 11
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
|
||||
version: 10
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.mat.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.mat.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77bcf914247406744892f91d7497c54e
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.png
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.png
(Stored with Git LFS)
Normal file
Binary file not shown.
117
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.png.meta
Normal file
117
Assets/ResourcesData/Prop/돈주머니/Particle/Coin.png.meta
Normal file
@ -0,0 +1,117 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08c6cf6250054064fb85c4f953406a6f
|
||||
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: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
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: 0
|
||||
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: 4
|
||||
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: 4
|
||||
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
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Prefab.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Prefab.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac3169f25f136984e9eda821c9b67868
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Prefab/돈주머니.prefab
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Prefab/돈주머니.prefab
(Stored with Git LFS)
Normal file
Binary file not shown.
7
Assets/ResourcesData/Prop/돈주머니/Prefab/돈주머니.prefab.meta
Normal file
7
Assets/ResourcesData/Prop/돈주머니/Prefab/돈주머니.prefab.meta
Normal file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a438ec402234494db1c4597ce20dc41
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Sound.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Sound.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec9ad2e9fbffb604184a0ee84836d48b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Sound/돈주머니 효과음.mp3
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Sound/돈주머니 효과음.mp3
(Stored with Git LFS)
Normal file
Binary file not shown.
23
Assets/ResourcesData/Prop/돈주머니/Sound/돈주머니 효과음.mp3.meta
Normal file
23
Assets/ResourcesData/Prop/돈주머니/Sound/돈주머니 효과음.mp3.meta
Normal file
@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 483113300e6fec0488ef74bbb771c3da
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 8
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
conversionMode: 0
|
||||
preloadAudioData: 0
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/ResourcesData/Prop/돈주머니/Thumbnail.meta
Normal file
8
Assets/ResourcesData/Prop/돈주머니/Thumbnail.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0597c595d70f57242b02e1f8ecd7f88e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/ResourcesData/Prop/돈주머니/Thumbnail/돈주머니_thumbnail.png
(Stored with Git LFS)
Normal file
BIN
Assets/ResourcesData/Prop/돈주머니/Thumbnail/돈주머니_thumbnail.png
(Stored with Git LFS)
Normal file
Binary file not shown.
117
Assets/ResourcesData/Prop/돈주머니/Thumbnail/돈주머니_thumbnail.png.meta
Normal file
117
Assets/ResourcesData/Prop/돈주머니/Thumbnail/돈주머니_thumbnail.png.meta
Normal file
@ -0,0 +1,117 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52eff15a79fe4224993692534a200c90
|
||||
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: 0
|
||||
wrapV: 0
|
||||
wrapW: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
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: 0
|
||||
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: 4
|
||||
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: 4
|
||||
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
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID:
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/Streamingle/DonationThrowable.meta
Normal file
8
Assets/Scripts/Streamingle/DonationThrowable.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd54180499ff72f4bbcb70fe47ba5f8a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
315
Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs
Normal file
315
Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs
Normal file
@ -0,0 +1,315 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Streamingle
|
||||
{
|
||||
/// <summary>
|
||||
/// Component attached to throwable objects.
|
||||
/// Uses parabolic interpolation for smooth arc trajectory.
|
||||
/// On reaching target, enables collision detection - bounces off colliders or falls.
|
||||
/// </summary>
|
||||
public class ThrowableObject : MonoBehaviour
|
||||
{
|
||||
[HideInInspector]
|
||||
public ThrowableObjectLauncher launcher;
|
||||
|
||||
[Header("Settings")]
|
||||
[Tooltip("Play sound on hit")]
|
||||
public AudioClip[] hitSounds;
|
||||
|
||||
[Tooltip("Spawn particle effect on hit")]
|
||||
public GameObject hitEffectPrefab;
|
||||
|
||||
[Tooltip("Destroy hit effect after this time")]
|
||||
public float hitEffectLifetime = 2f;
|
||||
|
||||
[Header("Trajectory Settings")]
|
||||
[Tooltip("Time to reach target in seconds")]
|
||||
public float flightDuration = 1.5f;
|
||||
|
||||
[Tooltip("Arc height (higher = more curved trajectory)")]
|
||||
public float arcHeight = 2f;
|
||||
|
||||
[Tooltip("Rotation speed while flying")]
|
||||
public float rotationSpeed = 180f;
|
||||
|
||||
[Header("Collision Settings")]
|
||||
[Tooltip("Bounce force multiplier when hitting collider")]
|
||||
public float bounceForce = 5f;
|
||||
|
||||
[Tooltip("Additional upward force on bounce")]
|
||||
public float bounceUpForce = 2f;
|
||||
|
||||
[Tooltip("Time to stay after reaching target before deactivating")]
|
||||
public float postArrivalLifetime = 3f;
|
||||
|
||||
// Trajectory state
|
||||
private Vector3 startPosition;
|
||||
private Vector3 targetPosition;
|
||||
private Transform targetTransform;
|
||||
private float flightTime;
|
||||
private float currentTime;
|
||||
private bool isFlying = false;
|
||||
private bool hasArrived = false;
|
||||
private bool hasCollided = false;
|
||||
private Vector3 rotationAxis;
|
||||
private Vector3 arrivalVelocity;
|
||||
|
||||
// Components
|
||||
private Rigidbody rb;
|
||||
private AudioSource audioSource;
|
||||
private Collider[] colliders;
|
||||
|
||||
// Lifetime
|
||||
private float lifetime;
|
||||
private float spawnTime;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
rb = GetComponent<Rigidbody>();
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
colliders = GetComponents<Collider>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the throwable object with target position
|
||||
/// </summary>
|
||||
public void Initialize(Transform target, float lifetime)
|
||||
{
|
||||
Initialize(target, lifetime, Vector3.zero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize with offset for target position
|
||||
/// </summary>
|
||||
public void Initialize(Transform target, float lifetime, Vector3 targetOffset)
|
||||
{
|
||||
this.lifetime = lifetime;
|
||||
this.spawnTime = Time.time;
|
||||
this.hasArrived = false;
|
||||
this.hasCollided = false;
|
||||
this.isFlying = true;
|
||||
this.currentTime = 0f;
|
||||
this.flightTime = flightDuration;
|
||||
this.startPosition = transform.position;
|
||||
this.targetTransform = target;
|
||||
|
||||
// Calculate target position with offset
|
||||
if (target != null)
|
||||
{
|
||||
this.targetPosition = target.position + targetOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.targetPosition = transform.position + Vector3.forward * 5f;
|
||||
}
|
||||
|
||||
// Random rotation axis for spinning effect
|
||||
rotationAxis = Random.insideUnitSphere.normalized;
|
||||
|
||||
// Disable physics during flight
|
||||
if (rb != null)
|
||||
{
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
}
|
||||
|
||||
// Disable colliders during flight
|
||||
SetCollidersEnabled(false);
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Check lifetime
|
||||
if (lifetime > 0 && Time.time - spawnTime > lifetime)
|
||||
{
|
||||
Deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFlying) return;
|
||||
|
||||
// Store previous position for velocity calculation
|
||||
Vector3 prevPos = transform.position;
|
||||
|
||||
currentTime += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(currentTime / flightTime);
|
||||
|
||||
// Calculate parabolic position
|
||||
Vector3 currentPos = CalculateParabolicPosition(t);
|
||||
transform.position = currentPos;
|
||||
|
||||
// Calculate velocity for when we switch to physics
|
||||
if (Time.deltaTime > 0)
|
||||
{
|
||||
arrivalVelocity = (currentPos - prevPos) / Time.deltaTime;
|
||||
}
|
||||
|
||||
// Rotate while flying
|
||||
transform.Rotate(rotationAxis, rotationSpeed * Time.deltaTime, Space.World);
|
||||
|
||||
// Check if reached target
|
||||
if (t >= 1f)
|
||||
{
|
||||
OnReachedTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate position along parabolic arc
|
||||
/// </summary>
|
||||
private Vector3 CalculateParabolicPosition(float t)
|
||||
{
|
||||
// Linear interpolation for base position
|
||||
Vector3 linearPos = Vector3.Lerp(startPosition, targetPosition, t);
|
||||
|
||||
// Parabolic arc (peaks at t=0.5)
|
||||
float parabola = 4f * arcHeight * t * (1f - t);
|
||||
|
||||
// Add arc height to Y position
|
||||
linearPos.y += parabola;
|
||||
|
||||
return linearPos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when object reaches the target position
|
||||
/// </summary>
|
||||
private void OnReachedTarget()
|
||||
{
|
||||
isFlying = false;
|
||||
hasArrived = true;
|
||||
|
||||
// Re-enable colliders for collision detection
|
||||
SetCollidersEnabled(true);
|
||||
|
||||
// Enable physics - object will either hit a collider and bounce or fall
|
||||
if (rb != null)
|
||||
{
|
||||
rb.isKinematic = false;
|
||||
rb.useGravity = true;
|
||||
|
||||
// Continue with the arrival velocity (maintains momentum)
|
||||
rb.linearVelocity = arrivalVelocity;
|
||||
rb.angularVelocity = rotationAxis * rotationSpeed * Mathf.Deg2Rad;
|
||||
}
|
||||
|
||||
// Schedule deactivation (object falls if no collision)
|
||||
Invoke(nameof(Deactivate), postArrivalLifetime);
|
||||
}
|
||||
|
||||
void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
if (!hasArrived || hasCollided) return;
|
||||
|
||||
// Check if collision is with the target
|
||||
bool isTargetHit = false;
|
||||
if (targetTransform != null)
|
||||
{
|
||||
// Check if collided object is the target or a child of target
|
||||
Transform hitTransform = collision.collider.transform;
|
||||
isTargetHit = hitTransform == targetTransform || hitTransform.IsChildOf(targetTransform);
|
||||
}
|
||||
|
||||
hasCollided = true;
|
||||
|
||||
// Only notify launcher and play effects if hit the target
|
||||
if (isTargetHit)
|
||||
{
|
||||
if (launcher != null)
|
||||
{
|
||||
launcher.OnObjectHitTarget(gameObject, collision.collider, collision);
|
||||
}
|
||||
|
||||
// Play hit sound
|
||||
PlayHitSound();
|
||||
|
||||
// Spawn hit effect
|
||||
SpawnHitEffect(collision);
|
||||
}
|
||||
|
||||
// Apply bounce force regardless of what was hit
|
||||
if (rb != null && collision.contacts.Length > 0)
|
||||
{
|
||||
Vector3 normal = collision.contacts[0].normal;
|
||||
Vector3 reflectedVel = Vector3.Reflect(rb.linearVelocity, normal);
|
||||
|
||||
// Apply bounce with some force
|
||||
rb.linearVelocity = reflectedVel.normalized * bounceForce + Vector3.up * bounceUpForce;
|
||||
rb.angularVelocity = Random.insideUnitSphere * 10f;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCollidersEnabled(bool enabled)
|
||||
{
|
||||
if (colliders == null) return;
|
||||
foreach (var col in colliders)
|
||||
{
|
||||
if (col != null) col.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayHitSound()
|
||||
{
|
||||
if (hitSounds == null || hitSounds.Length == 0) return;
|
||||
|
||||
AudioClip clip = hitSounds[Random.Range(0, hitSounds.Length)];
|
||||
if (clip == null) return;
|
||||
|
||||
if (audioSource != null)
|
||||
{
|
||||
audioSource.PlayOneShot(clip);
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnHitEffect(Collision collision = null)
|
||||
{
|
||||
if (hitEffectPrefab == null) return;
|
||||
|
||||
Vector3 pos = transform.position;
|
||||
Quaternion rot = Quaternion.identity;
|
||||
|
||||
if (collision != null && collision.contacts.Length > 0)
|
||||
{
|
||||
pos = collision.contacts[0].point;
|
||||
rot = Quaternion.LookRotation(collision.contacts[0].normal);
|
||||
}
|
||||
|
||||
GameObject effect = Instantiate(hitEffectPrefab, pos, rot);
|
||||
if (hitEffectLifetime > 0)
|
||||
{
|
||||
Destroy(effect, hitEffectLifetime);
|
||||
}
|
||||
}
|
||||
|
||||
private void Deactivate()
|
||||
{
|
||||
CancelInvoke();
|
||||
isFlying = false;
|
||||
hasArrived = false;
|
||||
hasCollided = false;
|
||||
|
||||
if (launcher != null && launcher.usePooling)
|
||||
{
|
||||
// Return to pool
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destroy
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
CancelInvoke();
|
||||
isFlying = false;
|
||||
hasArrived = false;
|
||||
hasCollided = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53f705dd7db28b4489129cbca2dfe7b7
|
||||
@ -0,0 +1,340 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Streamingle
|
||||
{
|
||||
/// <summary>
|
||||
/// Launches throwable objects at a target (avatar's head).
|
||||
/// Connect to WefLab donation events to throw objects on donations.
|
||||
/// Uses parabolic interpolation for smooth arc trajectories.
|
||||
/// </summary>
|
||||
public class ThrowableObjectLauncher : MonoBehaviour
|
||||
{
|
||||
[Header("Target")]
|
||||
[Tooltip("The target transform to throw objects at (usually avatar's head)")]
|
||||
public Transform target;
|
||||
|
||||
[Tooltip("Fixed offset from target position")]
|
||||
public Vector3 targetOffset = Vector3.zero;
|
||||
|
||||
[Tooltip("Random offset range for hit position")]
|
||||
public Vector3 targetRandomOffset = new Vector3(0.1f, 0.1f, 0.1f);
|
||||
|
||||
[Header("Target Collider")]
|
||||
[Tooltip("Automatically create a collider on target for bounce detection")]
|
||||
public bool autoCreateTargetCollider = true;
|
||||
|
||||
[Tooltip("Radius of the auto-created sphere collider")]
|
||||
public float targetColliderRadius = 0.15f;
|
||||
|
||||
[Header("Spawn Settings")]
|
||||
[Tooltip("Prefabs to throw (randomly selected)")]
|
||||
public GameObject[] throwablePrefabs;
|
||||
|
||||
[Tooltip("Spawn position offset from this transform")]
|
||||
public Vector3 spawnOffset = new Vector3(0, 1f, 0);
|
||||
|
||||
[Tooltip("Randomize spawn position within this range")]
|
||||
public Vector3 spawnRandomRange = new Vector3(5f, 1f, 5f);
|
||||
|
||||
[Header("Throw Settings")]
|
||||
[Tooltip("Delay between throws when throwing multiple")]
|
||||
public float throwInterval = 0.15f;
|
||||
|
||||
[Header("Object Lifetime")]
|
||||
[Tooltip("Destroy thrown objects after this time (0 = never)")]
|
||||
public float objectLifetime = 5f;
|
||||
|
||||
[Tooltip("Use object pooling instead of instantiate/destroy")]
|
||||
public bool usePooling = true;
|
||||
|
||||
[Tooltip("Pool size per prefab")]
|
||||
public int poolSizePerPrefab = 10;
|
||||
|
||||
[Header("Events")]
|
||||
public UnityEvent<GameObject> onObjectThrown;
|
||||
public UnityEvent<GameObject, Collision> onObjectHit;
|
||||
|
||||
// Object pool
|
||||
private GameObject[][] objectPool;
|
||||
private int[] poolIndices;
|
||||
|
||||
// Auto-created collider
|
||||
private SphereCollider createdTargetCollider;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (usePooling)
|
||||
{
|
||||
InitializePool();
|
||||
}
|
||||
|
||||
if (autoCreateTargetCollider && target != null)
|
||||
{
|
||||
SetupTargetCollider();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupTargetCollider()
|
||||
{
|
||||
// Check if target already has a collider
|
||||
var existingCollider = target.GetComponent<Collider>();
|
||||
if (existingCollider != null)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[ThrowableObjectLauncher] Target already has collider: {existingCollider.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create sphere collider on target with offset applied
|
||||
createdTargetCollider = target.gameObject.AddComponent<SphereCollider>();
|
||||
createdTargetCollider.radius = targetColliderRadius;
|
||||
// Apply targetOffset to collider center (convert world offset to local space)
|
||||
createdTargetCollider.center = target.InverseTransformDirection(targetOffset);
|
||||
|
||||
// Ensure target has rigidbody (kinematic) for collision detection
|
||||
var rb = target.GetComponent<Rigidbody>();
|
||||
if (rb == null)
|
||||
{
|
||||
rb = target.gameObject.AddComponent<Rigidbody>();
|
||||
rb.isKinematic = true;
|
||||
rb.useGravity = false;
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log($"[ThrowableObjectLauncher] Created SphereCollider on target: {target.name}, radius: {targetColliderRadius}, center: {createdTargetCollider.center}");
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// Clean up created collider
|
||||
if (createdTargetCollider != null)
|
||||
{
|
||||
Destroy(createdTargetCollider);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePool()
|
||||
{
|
||||
if (throwablePrefabs == null || throwablePrefabs.Length == 0) return;
|
||||
|
||||
objectPool = new GameObject[throwablePrefabs.Length][];
|
||||
poolIndices = new int[throwablePrefabs.Length];
|
||||
|
||||
for (int i = 0; i < throwablePrefabs.Length; i++)
|
||||
{
|
||||
if (throwablePrefabs[i] == null) continue;
|
||||
|
||||
objectPool[i] = new GameObject[poolSizePerPrefab];
|
||||
for (int j = 0; j < poolSizePerPrefab; j++)
|
||||
{
|
||||
var obj = Instantiate(throwablePrefabs[i], transform);
|
||||
obj.SetActive(false);
|
||||
|
||||
// Add throwable component if not present
|
||||
var throwable = obj.GetComponent<ThrowableObject>();
|
||||
if (throwable == null)
|
||||
{
|
||||
throwable = obj.AddComponent<ThrowableObject>();
|
||||
}
|
||||
throwable.launcher = this;
|
||||
|
||||
objectPool[i][j] = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw a random object at the target
|
||||
/// </summary>
|
||||
public void ThrowObject()
|
||||
{
|
||||
ThrowObject(Random.Range(0, throwablePrefabs.Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw a specific object at the target
|
||||
/// </summary>
|
||||
public void ThrowObject(int prefabIndex)
|
||||
{
|
||||
if (throwablePrefabs == null || throwablePrefabs.Length == 0)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[ThrowableObjectLauncher] No throwable prefabs assigned");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[ThrowableObjectLauncher] No target assigned");
|
||||
return;
|
||||
}
|
||||
|
||||
prefabIndex = Mathf.Clamp(prefabIndex, 0, throwablePrefabs.Length - 1);
|
||||
if (throwablePrefabs[prefabIndex] == null) return;
|
||||
|
||||
// Get or create object
|
||||
GameObject obj = GetObject(prefabIndex);
|
||||
if (obj == null) return;
|
||||
|
||||
// Calculate spawn position
|
||||
Vector3 spawnPos = transform.position + transform.TransformDirection(spawnOffset);
|
||||
spawnPos += new Vector3(
|
||||
Random.Range(-spawnRandomRange.x, spawnRandomRange.x),
|
||||
Random.Range(-spawnRandomRange.y, spawnRandomRange.y),
|
||||
Random.Range(-spawnRandomRange.z, spawnRandomRange.z)
|
||||
);
|
||||
|
||||
// Calculate random offset for target position
|
||||
Vector3 randomOffset = new Vector3(
|
||||
Random.Range(-targetRandomOffset.x, targetRandomOffset.x),
|
||||
Random.Range(-targetRandomOffset.y, targetRandomOffset.y),
|
||||
Random.Range(-targetRandomOffset.z, targetRandomOffset.z)
|
||||
);
|
||||
|
||||
// Combine fixed offset + random offset
|
||||
Vector3 combinedOffset = targetOffset + randomOffset;
|
||||
|
||||
// Setup object position
|
||||
obj.transform.position = spawnPos;
|
||||
obj.transform.rotation = Random.rotation;
|
||||
obj.SetActive(true);
|
||||
|
||||
// Setup throwable component with combined offset
|
||||
var throwable = obj.GetComponent<ThrowableObject>();
|
||||
if (throwable != null)
|
||||
{
|
||||
throwable.Initialize(target, objectLifetime, combinedOffset);
|
||||
}
|
||||
|
||||
onObjectThrown?.Invoke(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw multiple objects
|
||||
/// </summary>
|
||||
public void ThrowMultiple(int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Delay each throw slightly
|
||||
float delay = i * throwInterval;
|
||||
StartCoroutine(ThrowDelayed(delay));
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator ThrowDelayed(float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
ThrowObject();
|
||||
}
|
||||
|
||||
private GameObject GetObject(int prefabIndex)
|
||||
{
|
||||
GameObject obj;
|
||||
|
||||
if (usePooling && objectPool != null && objectPool[prefabIndex] != null)
|
||||
{
|
||||
// Find inactive object in pool
|
||||
for (int i = 0; i < poolSizePerPrefab; i++)
|
||||
{
|
||||
int idx = (poolIndices[prefabIndex] + i) % poolSizePerPrefab;
|
||||
if (!objectPool[prefabIndex][idx].activeInHierarchy)
|
||||
{
|
||||
poolIndices[prefabIndex] = (idx + 1) % poolSizePerPrefab;
|
||||
obj = objectPool[prefabIndex][idx];
|
||||
// Ensure launcher reference is set
|
||||
var throwable = obj.GetComponent<ThrowableObject>();
|
||||
if (throwable != null) throwable.launcher = this;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
// All in use, reuse oldest
|
||||
poolIndices[prefabIndex] = (poolIndices[prefabIndex] + 1) % poolSizePerPrefab;
|
||||
obj = objectPool[prefabIndex][poolIndices[prefabIndex]];
|
||||
obj.SetActive(false);
|
||||
// Ensure launcher reference is set
|
||||
var throwableOldest = obj.GetComponent<ThrowableObject>();
|
||||
if (throwableOldest != null) throwableOldest.launcher = this;
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Instantiate new
|
||||
obj = Instantiate(throwablePrefabs[prefabIndex]);
|
||||
var throwable = obj.GetComponent<ThrowableObject>();
|
||||
if (throwable == null)
|
||||
{
|
||||
throwable = obj.AddComponent<ThrowableObject>();
|
||||
}
|
||||
throwable.launcher = this;
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by ThrowableObject when it reaches the target
|
||||
/// </summary>
|
||||
public void OnObjectHitTarget(GameObject obj, Collider hitCollider, Collision collision)
|
||||
{
|
||||
onObjectHit?.Invoke(obj, collision);
|
||||
}
|
||||
|
||||
#region Context Menu Test Methods
|
||||
|
||||
[ContextMenu("Test: Throw 1")]
|
||||
private void TestThrowOne()
|
||||
{
|
||||
ThrowObject();
|
||||
}
|
||||
|
||||
[ContextMenu("Test: Throw 5")]
|
||||
private void TestThrowFive()
|
||||
{
|
||||
ThrowMultiple(5);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
// Draw spawn area
|
||||
Gizmos.color = Color.green;
|
||||
Vector3 spawnPos = transform.position + transform.TransformDirection(spawnOffset);
|
||||
Gizmos.DrawWireCube(spawnPos, spawnRandomRange * 2);
|
||||
|
||||
// Draw target
|
||||
if (target != null)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Vector3 targetPos = target.position + targetOffset;
|
||||
Gizmos.DrawWireSphere(targetPos, 0.2f);
|
||||
Gizmos.DrawWireCube(targetPos, targetRandomOffset * 2);
|
||||
|
||||
// Draw target collider radius (at offset position)
|
||||
if (autoCreateTargetCollider)
|
||||
{
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawWireSphere(target.position + targetOffset, targetColliderRadius);
|
||||
}
|
||||
|
||||
// Draw trajectory preview (parabolic arc)
|
||||
Gizmos.color = Color.yellow;
|
||||
DrawParabolicArc(spawnPos, targetPos, 2f, 20);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawParabolicArc(Vector3 start, Vector3 end, float arcHeight, int segments)
|
||||
{
|
||||
Vector3 prevPos = start;
|
||||
for (int i = 1; i <= segments; i++)
|
||||
{
|
||||
float t = i / (float)segments;
|
||||
Vector3 linearPos = Vector3.Lerp(start, end, t);
|
||||
float parabola = 4f * arcHeight * t * (1f - t);
|
||||
linearPos.y += parabola;
|
||||
|
||||
Gizmos.DrawLine(prevPos, linearPos);
|
||||
prevPos = linearPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85064059a4495fd48824a7a421bee446
|
||||
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Streamingle.Background
|
||||
@ -37,48 +36,4 @@ namespace Streamingle.Background
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 배경 씬 데이터를 저장하는 ScriptableObject
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "BackgroundSceneDatabase", menuName = "Streamingle/Background Scene Database")]
|
||||
public class BackgroundSceneDatabase : ScriptableObject
|
||||
{
|
||||
public List<BackgroundSceneInfo> scenes = new List<BackgroundSceneInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리별로 씬 목록 반환
|
||||
/// </summary>
|
||||
public Dictionary<string, List<BackgroundSceneInfo>> GetScenesByCategory()
|
||||
{
|
||||
var result = new Dictionary<string, List<BackgroundSceneInfo>>();
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
string category = scene.Category;
|
||||
if (!result.ContainsKey(category))
|
||||
{
|
||||
result[category] = new List<BackgroundSceneInfo>();
|
||||
}
|
||||
result[category].Add(scene);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 이름으로 검색
|
||||
/// </summary>
|
||||
public BackgroundSceneInfo FindByName(string sceneName)
|
||||
{
|
||||
return scenes.Find(s => s.sceneName == sceneName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 경로로 검색
|
||||
/// </summary>
|
||||
public BackgroundSceneInfo FindByPath(string scenePath)
|
||||
{
|
||||
return scenes.Find(s => s.scenePath == scenePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Streamingle.Background
|
||||
{
|
||||
/// <summary>
|
||||
/// 배경 씬 데이터를 저장하는 ScriptableObject
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "BackgroundSceneDatabase", menuName = "Streamingle/Background Scene Database")]
|
||||
public class BackgroundSceneDatabase : ScriptableObject
|
||||
{
|
||||
public List<BackgroundSceneInfo> scenes = new List<BackgroundSceneInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리별로 씬 목록 반환
|
||||
/// </summary>
|
||||
public Dictionary<string, List<BackgroundSceneInfo>> GetScenesByCategory()
|
||||
{
|
||||
var result = new Dictionary<string, List<BackgroundSceneInfo>>();
|
||||
|
||||
foreach (var scene in scenes)
|
||||
{
|
||||
string category = scene.Category;
|
||||
if (!result.ContainsKey(category))
|
||||
{
|
||||
result[category] = new List<BackgroundSceneInfo>();
|
||||
}
|
||||
result[category].Add(scene);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 이름으로 검색
|
||||
/// </summary>
|
||||
public BackgroundSceneInfo FindByName(string sceneName)
|
||||
{
|
||||
return scenes.Find(s => s.sceneName == sceneName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 경로로 검색
|
||||
/// </summary>
|
||||
public BackgroundSceneInfo FindByPath(string scenePath)
|
||||
{
|
||||
return scenes.Find(s => s.scenePath == scenePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee084dfc7f17cb7498423a57ca4bd971
|
||||
@ -1,4 +1,5 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -164,6 +165,11 @@ public class CameraManager : MonoBehaviour, IController
|
||||
[System.NonSerialized] public Vector3 savedFocusPoint;
|
||||
[System.NonSerialized] public bool hasOrbitState = false;
|
||||
|
||||
// 프리셋별 FOV 저장 (Alt+Q 복원용)
|
||||
[System.NonSerialized] public float initialFOV;
|
||||
[System.NonSerialized] public float savedFOV;
|
||||
[System.NonSerialized] public bool hasInitialFOV = false;
|
||||
|
||||
public CameraPreset(CinemachineCamera camera)
|
||||
{
|
||||
virtualCamera = camera;
|
||||
@ -181,6 +187,14 @@ public class CameraManager : MonoBehaviour, IController
|
||||
initialPosition = virtualCamera.transform.position;
|
||||
initialRotation = virtualCamera.transform.rotation;
|
||||
hasInitialState = true;
|
||||
|
||||
// FOV 초기값 저장
|
||||
if (virtualCamera.Lens.FieldOfView > 0)
|
||||
{
|
||||
initialFOV = virtualCamera.Lens.FieldOfView;
|
||||
savedFOV = initialFOV;
|
||||
hasInitialFOV = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 초기 상태 복원 (Alt+Q)
|
||||
@ -189,25 +203,36 @@ public class CameraManager : MonoBehaviour, IController
|
||||
if (!hasInitialState || virtualCamera == null) return;
|
||||
virtualCamera.transform.position = initialPosition;
|
||||
virtualCamera.transform.rotation = initialRotation;
|
||||
|
||||
// FOV 초기값 복원
|
||||
if (hasInitialFOV)
|
||||
{
|
||||
var lens = virtualCamera.Lens;
|
||||
lens.FieldOfView = initialFOV;
|
||||
virtualCamera.Lens = lens;
|
||||
savedFOV = initialFOV;
|
||||
}
|
||||
}
|
||||
|
||||
// orbit 상태 저장 (카메라 전환 시)
|
||||
public void SaveOrbitState(float hAngle, float vAngle, float dist, Vector3 focus)
|
||||
public void SaveOrbitState(float hAngle, float vAngle, float dist, Vector3 focus, float fov)
|
||||
{
|
||||
savedHorizontalAngle = hAngle;
|
||||
savedVerticalAngle = vAngle;
|
||||
savedDistance = dist;
|
||||
savedFocusPoint = focus;
|
||||
savedFOV = fov;
|
||||
hasOrbitState = true;
|
||||
}
|
||||
|
||||
// orbit 상태 복원 (카메라 전환 시)
|
||||
public bool TryRestoreOrbitState(out float hAngle, out float vAngle, out float dist, out Vector3 focus)
|
||||
public bool TryRestoreOrbitState(out float hAngle, out float vAngle, out float dist, out Vector3 focus, out float fov)
|
||||
{
|
||||
hAngle = savedHorizontalAngle;
|
||||
vAngle = savedVerticalAngle;
|
||||
dist = savedDistance;
|
||||
focus = savedFocusPoint;
|
||||
fov = savedFOV;
|
||||
return hasOrbitState;
|
||||
}
|
||||
}
|
||||
@ -235,6 +260,11 @@ public class CameraManager : MonoBehaviour, IController
|
||||
[SerializeField] private float minZoomDistance = 0.5f;
|
||||
[SerializeField] private float maxZoomDistance = 50f;
|
||||
|
||||
[Header("FOV Settings")]
|
||||
[SerializeField, Range(0.1f, 5f)] private float fovSensitivity = 1f;
|
||||
[SerializeField] private float minFOV = 1f;
|
||||
[SerializeField] private float maxFOV = 90f;
|
||||
|
||||
[Header("Rotation Target")]
|
||||
[Tooltip("체크하면 아바타 머리를 자동으로 찾아 회전 중심점으로 사용합니다.")]
|
||||
[SerializeField] private bool useAvatarHeadAsTarget = true;
|
||||
@ -246,9 +276,12 @@ public class CameraManager : MonoBehaviour, IController
|
||||
[SerializeField] private bool useBlendTransition = false;
|
||||
[Tooltip("블렌드 전환 시간 (초)")]
|
||||
[SerializeField, Range(0.1f, 2f)] private float blendTime = 0.5f;
|
||||
[Tooltip("실시간 블렌딩 (두 카메라 동시 렌더링). 비활성화 시 스냅샷 블렌딩 사용")]
|
||||
[SerializeField] private bool useRealtimeBlend = true;
|
||||
|
||||
// 블렌드용 렌더 텍스처와 카메라
|
||||
private RenderTexture blendRenderTexture;
|
||||
private RenderTexture prevCameraRenderTexture; // 실시간 블렌딩용 이전 카메라 렌더 텍스처
|
||||
private Camera blendCamera;
|
||||
|
||||
private CinemachineCamera currentCamera;
|
||||
@ -433,7 +466,6 @@ public class CameraManager : MonoBehaviour, IController
|
||||
avatarHeadTransform = animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
if (avatarHeadTransform != null)
|
||||
{
|
||||
Debug.Log($"[CameraManager] 아바타 머리를 회전 중심점으로 설정: {avatarHeadTransform.name}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -543,7 +575,7 @@ public class CameraManager : MonoBehaviour, IController
|
||||
|
||||
private void HandleInput()
|
||||
{
|
||||
// 입력 우선순위 처리: Orbit > AltRightZoom > Zoom > Rotation > Panning
|
||||
// 입력 우선순위 처리: Orbit > CtrlRightZoom > Zoom > FOV > Rotation > Panning
|
||||
if (inputHandler.IsOrbitActive())
|
||||
{
|
||||
HandleOrbiting();
|
||||
@ -556,6 +588,10 @@ public class CameraManager : MonoBehaviour, IController
|
||||
{
|
||||
HandleDragZoom();
|
||||
}
|
||||
else if (inputHandler.IsFOVActive())
|
||||
{
|
||||
HandleFOV();
|
||||
}
|
||||
else if (inputHandler.IsRightMouseHeld())
|
||||
{
|
||||
HandleRotation();
|
||||
@ -651,6 +687,24 @@ public class CameraManager : MonoBehaviour, IController
|
||||
targetDistance = Mathf.Clamp(targetDistance, minZoomDistance, maxZoomDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shift + 좌클릭/우클릭 드래그: FOV 조절 (위로 밀면 FOV 감소=줌인, 아래로 밀면 FOV 증가=줌아웃)
|
||||
/// </summary>
|
||||
private void HandleFOV()
|
||||
{
|
||||
if (currentCamera == null) return;
|
||||
|
||||
Vector2 delta = inputHandler.GetLookDelta();
|
||||
if (delta.sqrMagnitude < float.Epsilon) return;
|
||||
|
||||
// 위로 밀면 FOV 감소 (줌인 효과), 아래로 밀면 FOV 증가 (줌아웃 효과)
|
||||
float fovDelta = -delta.y * fovSensitivity;
|
||||
|
||||
var lens = currentCamera.Lens;
|
||||
lens.FieldOfView = Mathf.Clamp(lens.FieldOfView + fovDelta, minFOV, maxFOV);
|
||||
currentCamera.Lens = lens;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스무딩을 적용하여 카메라 위치 업데이트
|
||||
/// </summary>
|
||||
@ -742,8 +796,6 @@ public class CameraManager : MonoBehaviour, IController
|
||||
/// </summary>
|
||||
public void SetImmediate(int index)
|
||||
{
|
||||
Debug.Log($"[CameraManager] 카메라 {index}번으로 전환 시작 (총 {cameraPresets?.Count ?? 0}개)");
|
||||
|
||||
if (!ValidateCameraIndex(index)) return;
|
||||
|
||||
var newPreset = cameraPresets[index];
|
||||
@ -760,7 +812,8 @@ public class CameraManager : MonoBehaviour, IController
|
||||
// 이전 프리셋의 orbit 상태 저장
|
||||
if (oldPreset != null && oldPreset.allowMouseControl)
|
||||
{
|
||||
oldPreset.SaveOrbitState(targetHorizontalAngle, targetVerticalAngle, targetDistance, targetFocusPoint);
|
||||
float currentFOV = oldPreset.virtualCamera != null ? oldPreset.virtualCamera.Lens.FieldOfView : 60f;
|
||||
oldPreset.SaveOrbitState(targetHorizontalAngle, targetVerticalAngle, targetDistance, targetFocusPoint, currentFOV);
|
||||
}
|
||||
|
||||
currentPreset = newPreset;
|
||||
@ -770,7 +823,7 @@ public class CameraManager : MonoBehaviour, IController
|
||||
if (newPreset.hasOrbitState && newPreset.allowMouseControl)
|
||||
{
|
||||
// 저장된 orbit 상태 복원
|
||||
if (newPreset.TryRestoreOrbitState(out float hAngle, out float vAngle, out float dist, out Vector3 focus))
|
||||
if (newPreset.TryRestoreOrbitState(out float hAngle, out float vAngle, out float dist, out Vector3 focus, out float fov))
|
||||
{
|
||||
targetHorizontalAngle = hAngle;
|
||||
targetVerticalAngle = vAngle;
|
||||
@ -781,6 +834,14 @@ public class CameraManager : MonoBehaviour, IController
|
||||
verticalAngle = vAngle;
|
||||
currentDistance = dist;
|
||||
focusPoint = focus;
|
||||
|
||||
// FOV 복원
|
||||
if (newPreset.virtualCamera != null && fov > 0)
|
||||
{
|
||||
var lens = newPreset.virtualCamera.Lens;
|
||||
lens.FieldOfView = fov;
|
||||
newPreset.virtualCamera.Lens = lens;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -797,7 +858,6 @@ public class CameraManager : MonoBehaviour, IController
|
||||
streamDeckManager.NotifyCameraChanged();
|
||||
}
|
||||
|
||||
Debug.Log($"[CameraManager] 카메라 전환 완료: {newCameraName}");
|
||||
}
|
||||
|
||||
private bool ValidateCameraIndex(int index)
|
||||
@ -832,9 +892,10 @@ public class CameraManager : MonoBehaviour, IController
|
||||
return;
|
||||
}
|
||||
|
||||
// 블렌드용 카메라 생성
|
||||
GameObject blendCamObj = new GameObject("BlendCamera");
|
||||
blendCamObj.transform.SetParent(mainCamera.transform.parent);
|
||||
// 블렌드용 카메라 생성 - 독립적인 오브젝트로 (부모 없음)
|
||||
// Cinemachine Brain의 영향을 피하기 위해 부모를 설정하지 않음
|
||||
GameObject blendCamObj = new GameObject("BlendCamera_Independent");
|
||||
blendCamObj.transform.SetParent(null); // 부모 없이 루트에 배치
|
||||
blendCamera = blendCamObj.AddComponent<Camera>();
|
||||
|
||||
// 메인 카메라 설정 복사
|
||||
@ -860,7 +921,6 @@ public class CameraManager : MonoBehaviour, IController
|
||||
blendCameraData.volumeTrigger = mainCameraData.volumeTrigger;
|
||||
}
|
||||
|
||||
Debug.Log("[CameraManager] 블렌드 카메라 생성 완료 (URP 설정 포함)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -886,61 +946,73 @@ public class CameraManager : MonoBehaviour, IController
|
||||
Destroy(blendRenderTexture);
|
||||
}
|
||||
|
||||
// 새 렌더 텍스처 생성
|
||||
blendRenderTexture = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);
|
||||
// 새 렌더 텍스처 생성 - 색상 전용 (depth 없음), 리니어 색공간에서 올바른 블렌딩
|
||||
var descriptor = new RenderTextureDescriptor(width, height, RenderTextureFormat.ARGB32, 0); // depth = 0
|
||||
descriptor.sRGB = true; // sRGB 텍스처로 설정하여 리니어 파이프라인과 일치
|
||||
blendRenderTexture = new RenderTexture(descriptor);
|
||||
blendRenderTexture.name = "CameraBlendRT";
|
||||
blendRenderTexture.Create();
|
||||
|
||||
Debug.Log($"[CameraManager] 블렌드 렌더 텍스처 생성: {width}x{height}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 크로스 디졸브 블렌드 전환 코루틴
|
||||
/// </summary>
|
||||
private IEnumerator BlendTransitionCoroutine(int targetIndex, float duration)
|
||||
{
|
||||
if (useRealtimeBlend)
|
||||
{
|
||||
yield return RealtimeBlendTransitionCoroutine(targetIndex, duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return SnapshotBlendTransitionCoroutine(targetIndex, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 스냅샷 블렌딩 (이전 화면 정지)
|
||||
/// </summary>
|
||||
private IEnumerator SnapshotBlendTransitionCoroutine(int targetIndex, float duration)
|
||||
{
|
||||
isBlending = true;
|
||||
|
||||
// 블렌드 카메라/텍스처 준비
|
||||
EnsureBlendCamera();
|
||||
// 블렌드 텍스처 준비
|
||||
EnsureBlendRenderTexture();
|
||||
|
||||
if (blendCamera == null || blendRenderTexture == null)
|
||||
if (blendRenderTexture == null)
|
||||
{
|
||||
Debug.LogError("[CameraManager] 블렌드 카메라 또는 텍스처 생성 실패");
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 메인 카메라의 현재 위치/회전 복사 (Cinemachine이 제어하는 실제 카메라)
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera != null)
|
||||
// 렌더 패스에서 현재 프레임(포스트 프로세싱 적용 후)을 캡처하도록 요청
|
||||
CameraBlendController.RequestCapture(blendRenderTexture);
|
||||
|
||||
// 캡처가 완료될 때까지 대기 (다음 프레임 렌더링 후)
|
||||
yield return new WaitForEndOfFrame();
|
||||
yield return null; // 렌더 패스가 실행될 때까지 한 프레임 더 대기
|
||||
|
||||
// 캡처 완료 확인
|
||||
if (!CameraBlendController.CaptureReady)
|
||||
{
|
||||
blendCamera.transform.position = mainCamera.transform.position;
|
||||
blendCamera.transform.rotation = mainCamera.transform.rotation;
|
||||
blendCamera.fieldOfView = mainCamera.fieldOfView;
|
||||
CameraBlendController.EndBlend();
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 블렌드 카메라로 현재 화면을 렌더 텍스처에 렌더링
|
||||
blendCamera.targetTexture = blendRenderTexture;
|
||||
blendCamera.enabled = true;
|
||||
blendCamera.Render();
|
||||
blendCamera.enabled = false;
|
||||
// 블렌딩 시작 - BlendAmount = 1 (이전 카메라만 보임)
|
||||
CameraBlendController.StartBlendAfterCapture();
|
||||
CameraBlendController.BlendAmount = 1f;
|
||||
|
||||
Debug.Log($"[CameraManager] 블렌드 시작 - Duration: {duration}s");
|
||||
|
||||
// 블렌딩 시작 - BlendAmount = 0 (이전 카메라 A만 보임)
|
||||
CameraBlendController.StartBlend(blendRenderTexture);
|
||||
|
||||
// 한 프레임 대기 - 블렌딩이 적용된 상태로 렌더링되도록
|
||||
yield return null;
|
||||
|
||||
// 카메라 전환 (이제 BlendAmount=0이므로 A만 보임)
|
||||
// 카메라 전환 (이제 BlendAmount=1이므로 캡처된 이전 화면만 보임)
|
||||
SetImmediate(targetIndex);
|
||||
|
||||
// 블렌드 진행: 0 → 1 (A에서 B로)
|
||||
// 블렌드 진행: 1 → 0 (이전 카메라가 서서히 사라지고 새 카메라가 드러남)
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
@ -948,21 +1020,145 @@ public class CameraManager : MonoBehaviour, IController
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
// 부드러운 이징 적용 (SmoothStep)
|
||||
t = t * t * (3f - 2f * t);
|
||||
CameraBlendController.BlendAmount = t;
|
||||
// 1에서 0으로: 이전 카메라가 서서히 사라짐
|
||||
CameraBlendController.BlendAmount = 1f - t;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
CameraBlendController.BlendAmount = 1f;
|
||||
CameraBlendController.BlendAmount = 0f;
|
||||
|
||||
// 블렌딩 종료
|
||||
CameraBlendController.EndBlend();
|
||||
|
||||
Debug.Log("[CameraManager] 블렌드 완료");
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실시간 블렌딩 (두 카메라 동시 렌더링)
|
||||
/// 이전 카메라 위치를 저장해두고 그 위치에서 렌더링, 서서히 사라지면서 새 카메라가 드러남
|
||||
/// </summary>
|
||||
private IEnumerator RealtimeBlendTransitionCoroutine(int targetIndex, float duration)
|
||||
{
|
||||
isBlending = true;
|
||||
|
||||
// 이전 카메라 프리셋 저장
|
||||
var prevPreset = currentPreset;
|
||||
if (prevPreset == null || prevPreset.virtualCamera == null)
|
||||
{
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// SetImmediate 호출 전에 이전 카메라의 위치/회전/FOV를 저장
|
||||
Vector3 prevCameraPosition = prevPreset.virtualCamera.transform.position;
|
||||
Quaternion prevCameraRotation = prevPreset.virtualCamera.transform.rotation;
|
||||
float prevCameraFOV = prevPreset.virtualCamera.Lens.FieldOfView;
|
||||
|
||||
// 블렌드 텍스처 준비
|
||||
EnsureBlendRenderTexture();
|
||||
EnsurePrevCameraRenderTexture();
|
||||
|
||||
if (blendRenderTexture == null || prevCameraRenderTexture == null)
|
||||
{
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 블렌드 카메라 준비
|
||||
EnsureBlendCamera();
|
||||
if (blendCamera == null)
|
||||
{
|
||||
SetImmediate(targetIndex);
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 블렌드 카메라를 이전 카메라 위치로 설정하고 첫 프레임 렌더링
|
||||
blendCamera.transform.SetPositionAndRotation(prevCameraPosition, prevCameraRotation);
|
||||
blendCamera.fieldOfView = prevCameraFOV;
|
||||
blendCamera.targetTexture = prevCameraRenderTexture;
|
||||
|
||||
// Camera.Render()로 렌더링 (URP에서도 포스트 프로세싱 적용됨)
|
||||
blendCamera.Render();
|
||||
Graphics.Blit(prevCameraRenderTexture, blendRenderTexture);
|
||||
|
||||
// 실시간 블렌딩 시작 (BlendAmount = 1에서 시작, 이전 카메라가 100% 보임)
|
||||
CameraBlendController.StartRealtimeBlend(blendRenderTexture);
|
||||
CameraBlendController.BlendAmount = 1f;
|
||||
|
||||
// 새 카메라로 전환 (메인 카메라는 이제 새 위치에서 렌더링됨)
|
||||
SetImmediate(targetIndex);
|
||||
|
||||
// 블렌드 진행: 1 → 0 (이전 카메라 화면이 서서히 사라지고 새 카메라가 드러남)
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
// 이전 카메라 시점에서 먼저 렌더링 (메인 카메라 렌더링 전에)
|
||||
blendCamera.transform.SetPositionAndRotation(prevCameraPosition, prevCameraRotation);
|
||||
blendCamera.fieldOfView = prevCameraFOV;
|
||||
blendCamera.targetTexture = prevCameraRenderTexture;
|
||||
|
||||
// Camera.Render()로 렌더링 (URP에서도 포스트 프로세싱 적용됨)
|
||||
blendCamera.Render();
|
||||
Graphics.Blit(prevCameraRenderTexture, blendRenderTexture);
|
||||
|
||||
// 다음 프레임까지 대기
|
||||
yield return null;
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
// 부드러운 이징 적용 (SmoothStep)
|
||||
t = t * t * (3f - 2f * t);
|
||||
// 1에서 0으로: 이전 카메라가 서서히 사라짐
|
||||
CameraBlendController.BlendAmount = 1f - t;
|
||||
}
|
||||
|
||||
CameraBlendController.BlendAmount = 0f;
|
||||
|
||||
// 블렌딩 종료
|
||||
CameraBlendController.EndBlend();
|
||||
|
||||
isBlending = false;
|
||||
blendCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실시간 블렌딩용 이전 카메라 렌더 텍스처 생성/갱신
|
||||
/// </summary>
|
||||
private void EnsurePrevCameraRenderTexture()
|
||||
{
|
||||
int width = Screen.width;
|
||||
int height = Screen.height;
|
||||
|
||||
// 이미 적절한 크기의 텍스처가 있으면 재사용
|
||||
if (prevCameraRenderTexture != null &&
|
||||
prevCameraRenderTexture.width == width &&
|
||||
prevCameraRenderTexture.height == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 기존 텍스처 해제
|
||||
if (prevCameraRenderTexture != null)
|
||||
{
|
||||
prevCameraRenderTexture.Release();
|
||||
Destroy(prevCameraRenderTexture);
|
||||
}
|
||||
|
||||
// 새 렌더 텍스처 생성 - HDR 포맷 + depth 포함 (포스트 프로세싱 지원)
|
||||
var descriptor = new RenderTextureDescriptor(width, height, RenderTextureFormat.DefaultHDR, 24);
|
||||
descriptor.sRGB = false; // HDR은 리니어 포맷
|
||||
prevCameraRenderTexture = new RenderTexture(descriptor);
|
||||
prevCameraRenderTexture.name = "PrevCameraRT";
|
||||
prevCameraRenderTexture.Create();
|
||||
}
|
||||
|
||||
private void UpdateCameraPriorities(CinemachineCamera newCamera)
|
||||
{
|
||||
if (newCamera == null)
|
||||
|
||||
@ -17,6 +17,7 @@ public class CameraManagerEditor : Editor
|
||||
private bool showControlSettings = true;
|
||||
private bool showSmoothingSettings = true;
|
||||
private bool showZoomSettings = true;
|
||||
private bool showFOVSettings = true;
|
||||
private bool showRotationTarget = true;
|
||||
private bool showBlendSettings = true;
|
||||
|
||||
@ -29,10 +30,14 @@ public class CameraManagerEditor : Editor
|
||||
private SerializedProperty rotationSmoothingProp;
|
||||
private SerializedProperty minZoomDistanceProp;
|
||||
private SerializedProperty maxZoomDistanceProp;
|
||||
private SerializedProperty fovSensitivityProp;
|
||||
private SerializedProperty minFOVProp;
|
||||
private SerializedProperty maxFOVProp;
|
||||
private SerializedProperty useAvatarHeadAsTargetProp;
|
||||
private SerializedProperty manualRotationTargetProp;
|
||||
private SerializedProperty useBlendTransitionProp;
|
||||
private SerializedProperty blendTimeProp;
|
||||
private SerializedProperty useRealtimeBlendProp;
|
||||
|
||||
// 스타일
|
||||
private GUIStyle headerStyle;
|
||||
@ -52,10 +57,14 @@ public class CameraManagerEditor : Editor
|
||||
rotationSmoothingProp = serializedObject.FindProperty("rotationSmoothing");
|
||||
minZoomDistanceProp = serializedObject.FindProperty("minZoomDistance");
|
||||
maxZoomDistanceProp = serializedObject.FindProperty("maxZoomDistance");
|
||||
fovSensitivityProp = serializedObject.FindProperty("fovSensitivity");
|
||||
minFOVProp = serializedObject.FindProperty("minFOV");
|
||||
maxFOVProp = serializedObject.FindProperty("maxFOV");
|
||||
useAvatarHeadAsTargetProp = serializedObject.FindProperty("useAvatarHeadAsTarget");
|
||||
manualRotationTargetProp = serializedObject.FindProperty("manualRotationTarget");
|
||||
useBlendTransitionProp = serializedObject.FindProperty("useBlendTransition");
|
||||
blendTimeProp = serializedObject.FindProperty("blendTime");
|
||||
useRealtimeBlendProp = serializedObject.FindProperty("useRealtimeBlend");
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@ -126,6 +135,9 @@ public class CameraManagerEditor : Editor
|
||||
// 줌 제한 섹션
|
||||
DrawZoomLimitsSection();
|
||||
|
||||
// FOV 설정 섹션
|
||||
DrawFOVSettingsSection();
|
||||
|
||||
// 회전 타겟 섹션
|
||||
DrawRotationTargetSection();
|
||||
|
||||
@ -211,6 +223,35 @@ public class CameraManagerEditor : Editor
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawFOVSettingsSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
|
||||
showFOVSettings = EditorGUILayout.Foldout(showFOVSettings, "FOV Settings", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (showFOVSettings)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.PropertyField(fovSensitivityProp, new GUIContent("FOV 감도", "Shift + 드래그 시 FOV 변화 감도 (0.01 ~ 5)"));
|
||||
EditorGUILayout.PropertyField(minFOVProp, new GUIContent("최소 FOV", "카메라 최소 FOV (줌인 한계)"));
|
||||
EditorGUILayout.PropertyField(maxFOVProp, new GUIContent("최대 FOV", "카메라 최대 FOV (줌아웃 한계)"));
|
||||
|
||||
// 경고 표시
|
||||
if (minFOVProp.floatValue >= maxFOVProp.floatValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("최소 FOV는 최대 FOV보다 작아야 합니다.", MessageType.Warning);
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox("Shift + 좌클릭/우클릭 드래그로 FOV를 조절합니다.\n위로 밀면 줌인(FOV 감소), 아래로 밀면 줌아웃(FOV 증가)", MessageType.Info);
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void DrawRotationTargetSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(sectionBoxStyle);
|
||||
@ -254,11 +295,20 @@ public class CameraManagerEditor : Editor
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!useBlendTransitionProp.boolValue);
|
||||
EditorGUILayout.PropertyField(blendTimeProp, new GUIContent("블렌드 시간", "크로스 디졸브 소요 시간 (초)"));
|
||||
EditorGUILayout.PropertyField(useRealtimeBlendProp, new GUIContent("실시간 블렌딩", "두 카메라를 동시에 렌더링하며 블렌딩 (성능 비용 증가)"));
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (useBlendTransitionProp.boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("URP Renderer에 CameraBlendRendererFeature가 추가되어 있어야 합니다.", MessageType.Info);
|
||||
if (useRealtimeBlendProp.boolValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("실시간 모드: 블렌딩 중 두 카메라 모두 실시간으로 렌더링됩니다.\n성능 비용이 증가하지만 이전 카메라도 움직입니다.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("스냅샷 모드: 이전 화면을 캡처한 후 블렌딩합니다.\n성능 효율적이지만 이전 화면이 정지합니다.", MessageType.Info);
|
||||
}
|
||||
EditorGUILayout.HelpBox("URP Renderer에 CameraBlendRendererFeature가 추가되어 있어야 합니다.", MessageType.Warning);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
|
||||
@ -9,6 +9,7 @@ public class InputHandler : MonoBehaviour
|
||||
private bool isOrbitActive;
|
||||
private bool isZoomActive;
|
||||
private bool isCtrlRightZoomActive;
|
||||
private bool isFOVActive;
|
||||
|
||||
// 현재 활성화된 입력 모드 (충돌 방지)
|
||||
private InputMode currentMode = InputMode.None;
|
||||
@ -23,6 +24,7 @@ public class InputHandler : MonoBehaviour
|
||||
Orbit, // Alt + 우클릭 또는 Alt + 좌클릭
|
||||
Zoom, // Ctrl + 좌클릭
|
||||
CtrlRightZoom, // Ctrl + 우클릭
|
||||
FOV, // Shift + 좌클릭 또는 Shift + 우클릭
|
||||
Rotation, // 우클릭
|
||||
Pan // 휠클릭
|
||||
}
|
||||
@ -57,6 +59,7 @@ public class InputHandler : MonoBehaviour
|
||||
isOrbitActive = false;
|
||||
isZoomActive = false;
|
||||
isCtrlRightZoomActive = false;
|
||||
isFOVActive = false;
|
||||
isRightMouseHeld = false;
|
||||
isMiddleMouseHeld = false;
|
||||
lastScrollValue = 0f;
|
||||
@ -85,6 +88,7 @@ public class InputHandler : MonoBehaviour
|
||||
bool middleMouse = Input.GetMouseButton(2);
|
||||
bool altKey = Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt);
|
||||
bool ctrlKey = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
||||
bool shiftKey = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
|
||||
// 모든 마우스 버튼이 해제되면 모드 리셋
|
||||
if (!leftMouse && !rightMouse && !middleMouse)
|
||||
@ -107,6 +111,10 @@ public class InputHandler : MonoBehaviour
|
||||
{
|
||||
currentMode = InputMode.Zoom;
|
||||
}
|
||||
else if (shiftKey && (leftMouse || rightMouse))
|
||||
{
|
||||
currentMode = InputMode.FOV;
|
||||
}
|
||||
else if (rightMouse)
|
||||
{
|
||||
currentMode = InputMode.Rotation;
|
||||
@ -121,6 +129,7 @@ public class InputHandler : MonoBehaviour
|
||||
isOrbitActive = (currentMode == InputMode.Orbit) && (rightMouse || leftMouse) && altKey;
|
||||
isZoomActive = (currentMode == InputMode.Zoom) && leftMouse && ctrlKey;
|
||||
isCtrlRightZoomActive = (currentMode == InputMode.CtrlRightZoom) && rightMouse && ctrlKey;
|
||||
isFOVActive = (currentMode == InputMode.FOV) && (leftMouse || rightMouse) && shiftKey;
|
||||
isRightMouseHeld = (currentMode == InputMode.Rotation) && rightMouse;
|
||||
isMiddleMouseHeld = (currentMode == InputMode.Pan) && middleMouse;
|
||||
|
||||
@ -137,6 +146,10 @@ public class InputHandler : MonoBehaviour
|
||||
{
|
||||
currentMode = InputMode.None;
|
||||
}
|
||||
else if (currentMode == InputMode.FOV && ((!leftMouse && !rightMouse) || !shiftKey))
|
||||
{
|
||||
currentMode = InputMode.None;
|
||||
}
|
||||
else if (currentMode == InputMode.Rotation && !rightMouse)
|
||||
{
|
||||
currentMode = InputMode.None;
|
||||
@ -156,6 +169,7 @@ public class InputHandler : MonoBehaviour
|
||||
public bool IsOrbitActive() => isOrbitActive;
|
||||
public bool IsZoomActive() => isZoomActive;
|
||||
public bool IsCtrlRightZoomActive() => isCtrlRightZoomActive;
|
||||
public bool IsFOVActive() => isFOVActive;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 어떤 입력 모드도 활성화되지 않았는지 확인합니다.
|
||||
|
||||
@ -726,9 +726,14 @@ namespace Streamingle.Prop.Editor
|
||||
}
|
||||
|
||||
if (ext == ".prefab")
|
||||
{
|
||||
// Prefab 폴더 안에 있는 프리팹만 수집 (Particle 등 다른 폴더 제외)
|
||||
string parentFolder = Path.GetFileName(Path.GetDirectoryName(file));
|
||||
if (parentFolder.Equals("Prefab", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
propInfo.prefabPaths.Add(assetPath);
|
||||
}
|
||||
}
|
||||
else if (ext == ".glb" || ext == ".fbx" || ext == ".obj")
|
||||
{
|
||||
propInfo.modelPaths.Add(assetPath);
|
||||
|
||||
@ -33,7 +33,7 @@ Material:
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _BlendAmount: 1
|
||||
- _BlendAmount: 0.000075280666
|
||||
m_Colors: []
|
||||
m_BuildTextureStacks: []
|
||||
m_AllowLocking: 1
|
||||
|
||||
@ -37,14 +37,15 @@ Shader "Streamingle/CameraBlend"
|
||||
{
|
||||
float2 uv = input.texcoord;
|
||||
|
||||
// 현재 카메라 (Blitter에서 전달됨)
|
||||
// 현재 카메라 (Blitter에서 전달됨) - 동일한 샘플러 사용
|
||||
float4 currentColor = SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp, uv);
|
||||
|
||||
// 이전 카메라 (캡처된 텍스처)
|
||||
float4 prevColor = SAMPLE_TEXTURE2D(_PrevTex, sampler_PrevTex, uv);
|
||||
// 이전 카메라 (캡처된 텍스처) - 동일한 Linear 샘플러 사용
|
||||
float4 prevColor = SAMPLE_TEXTURE2D(_PrevTex, sampler_LinearClamp, uv);
|
||||
|
||||
// 두 카메라를 블렌딩 (BlendAmount: 0 = 이전, 1 = 현재)
|
||||
float4 finalColor = lerp(prevColor, currentColor, _BlendAmount);
|
||||
// 리니어 공간에서 블렌딩 (BlendAmount: 1 = 이전, 0 = 현재)
|
||||
// 이전 카메라(B)가 위에 덮이고 서서히 사라짐
|
||||
float4 finalColor = lerp(currentColor, prevColor, _BlendAmount);
|
||||
|
||||
return finalColor;
|
||||
}
|
||||
|
||||
@ -12,8 +12,8 @@ public class CameraBlendRendererFeature : ScriptableRendererFeature
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
// BeforeRenderingPostProcessing으로 변경 - 포스트 프로세싱 전에 블렌딩
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
// AfterRenderingPostProcessing - 모든 포스트 프로세싱 적용 후 블렌딩
|
||||
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
|
||||
public Material blendMaterial;
|
||||
}
|
||||
|
||||
@ -35,8 +35,9 @@ public class CameraBlendRendererFeature : ScriptableRendererFeature
|
||||
if (renderingData.cameraData.cameraType != CameraType.Game)
|
||||
return;
|
||||
|
||||
// 블렌딩이 활성화된 경우에만 패스 추가
|
||||
if (CameraBlendController.IsBlending && CameraBlendController.BlendTexture != null)
|
||||
// 캡처 요청이 있거나 블렌딩 중일 때 패스 추가
|
||||
if (CameraBlendController.CaptureRequested ||
|
||||
(CameraBlendController.IsBlending && CameraBlendController.BlendTexture != null))
|
||||
{
|
||||
renderer.EnqueuePass(blendPass);
|
||||
}
|
||||
@ -63,6 +64,12 @@ public class CameraBlendRenderPass : ScriptableRenderPass
|
||||
public TextureHandle destinationTexture;
|
||||
}
|
||||
|
||||
private class CapturePassData
|
||||
{
|
||||
public TextureHandle sourceTexture;
|
||||
public RenderTexture targetTexture;
|
||||
}
|
||||
|
||||
public CameraBlendRenderPass(CameraBlendRendererFeature.Settings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
@ -75,16 +82,12 @@ public class CameraBlendRenderPass : ScriptableRenderPass
|
||||
if (settings.blendMaterial == null)
|
||||
return;
|
||||
|
||||
if (!CameraBlendController.IsBlending || CameraBlendController.BlendTexture == null)
|
||||
return;
|
||||
|
||||
var resourceData = frameData.Get<UniversalResourceData>();
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
|
||||
// 백버퍼인 경우 스킵 (BeforeRenderingPostProcessing에서는 일반적으로 false)
|
||||
// 백버퍼인 경우 스킵
|
||||
if (resourceData.isActiveTargetBackBuffer)
|
||||
{
|
||||
Debug.LogWarning("[CameraBlend] isActiveTargetBackBuffer - skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,10 +96,36 @@ public class CameraBlendRenderPass : ScriptableRenderPass
|
||||
// source가 유효한지 확인
|
||||
if (!source.IsValid())
|
||||
{
|
||||
Debug.LogWarning("[CameraBlend] source texture is not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
// 캡처 요청 처리 - 현재 프레임(포스트 프로세싱 적용 후)을 캡처
|
||||
if (CameraBlendController.CaptureRequested && CameraBlendController.BlendTexture != null)
|
||||
{
|
||||
// UnsafePass를 사용하여 RenderTexture에 직접 복사
|
||||
using (var builder = renderGraph.AddUnsafePass<CapturePassData>("Camera Blend Capture", out var captureData, profilingSampler))
|
||||
{
|
||||
captureData.sourceTexture = source;
|
||||
captureData.targetTexture = CameraBlendController.BlendTexture;
|
||||
|
||||
builder.UseTexture(source, AccessFlags.Read);
|
||||
builder.AllowPassCulling(false);
|
||||
|
||||
builder.SetRenderFunc((CapturePassData data, UnsafeGraphContext context) =>
|
||||
{
|
||||
// NativeCommandBuffer를 통해 Blit 수행
|
||||
var nativeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
nativeCmd.Blit(data.sourceTexture, data.targetTexture);
|
||||
CameraBlendController.CaptureReady = true;
|
||||
});
|
||||
}
|
||||
return; // 캡처만 하고 블렌딩은 다음 프레임부터
|
||||
}
|
||||
|
||||
// 블렌딩이 활성화되지 않았으면 스킵
|
||||
if (!CameraBlendController.IsBlending || CameraBlendController.BlendTexture == null)
|
||||
return;
|
||||
|
||||
// cameraColorDesc 사용 - 카메라 색상 텍스처 설명자
|
||||
var cameraTargetDesc = renderGraph.GetTextureDesc(resourceData.cameraColor);
|
||||
cameraTargetDesc.name = "_CameraBlendDestination";
|
||||
@ -158,6 +187,9 @@ public static class CameraBlendController
|
||||
private static bool isBlending = false;
|
||||
private static float blendAmount = 1f;
|
||||
private static RenderTexture blendTexture;
|
||||
private static bool captureRequested = false;
|
||||
private static bool captureReady = false;
|
||||
private static bool isRealtimeMode = false;
|
||||
|
||||
public static bool IsBlending
|
||||
{
|
||||
@ -177,6 +209,63 @@ public static class CameraBlendController
|
||||
set => blendTexture = value;
|
||||
}
|
||||
|
||||
public static bool CaptureRequested
|
||||
{
|
||||
get => captureRequested;
|
||||
set => captureRequested = value;
|
||||
}
|
||||
|
||||
public static bool CaptureReady
|
||||
{
|
||||
get => captureReady;
|
||||
set => captureReady = value;
|
||||
}
|
||||
|
||||
public static bool IsRealtimeMode
|
||||
{
|
||||
get => isRealtimeMode;
|
||||
set => isRealtimeMode = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 다음 프레임에서 현재 화면을 캡처하도록 요청합니다. (스냅샷 모드)
|
||||
/// </summary>
|
||||
public static void RequestCapture(RenderTexture targetTexture)
|
||||
{
|
||||
blendTexture = targetTexture;
|
||||
captureRequested = true;
|
||||
captureReady = false;
|
||||
isRealtimeMode = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 캡처가 완료된 후 블렌딩을 시작합니다. (스냅샷 모드)
|
||||
/// BlendAmount = 1에서 시작 (이전 카메라 100% 보임)
|
||||
/// </summary>
|
||||
public static void StartBlendAfterCapture()
|
||||
{
|
||||
if (captureReady && blendTexture != null)
|
||||
{
|
||||
blendAmount = 1f;
|
||||
isBlending = true;
|
||||
captureRequested = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 실시간 블렌딩을 시작합니다. (매 프레임 이전 카메라 렌더링)
|
||||
/// BlendAmount = 1에서 시작 (이전 카메라 100% 보임)
|
||||
/// </summary>
|
||||
public static void StartRealtimeBlend(RenderTexture texture)
|
||||
{
|
||||
blendTexture = texture;
|
||||
blendAmount = 1f;
|
||||
isBlending = true;
|
||||
isRealtimeMode = true;
|
||||
captureRequested = false;
|
||||
captureReady = false;
|
||||
}
|
||||
|
||||
public static void StartBlend(RenderTexture texture)
|
||||
{
|
||||
blendTexture = texture;
|
||||
@ -189,6 +278,9 @@ public static class CameraBlendController
|
||||
isBlending = false;
|
||||
blendAmount = 1f;
|
||||
blendTexture = null;
|
||||
captureRequested = false;
|
||||
captureReady = false;
|
||||
isRealtimeMode = false;
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
|
||||
@ -47,14 +47,29 @@ namespace WefLab
|
||||
[Tooltip("Maximum donation amount (inclusive, -1 for unlimited)")]
|
||||
public int maxAmount = -1;
|
||||
|
||||
[Header("Event Settings")]
|
||||
[Tooltip("Number of times to repeat the event (minimum 1)")]
|
||||
[Header("Repeat Settings")]
|
||||
[Tooltip("Use amount-based repeat count (divide amount by amountPerRepeat)")]
|
||||
public bool useAmountBasedRepeat = false;
|
||||
|
||||
[Tooltip("Amount of KRW per repeat (e.g., 1000 = 1 repeat per 1000 KRW)")]
|
||||
[Min(1)]
|
||||
public int repeatCount = 1;
|
||||
public int amountPerRepeat = 1000;
|
||||
|
||||
[Tooltip("Minimum repeat count")]
|
||||
[Min(1)]
|
||||
public int minRepeatCount = 1;
|
||||
|
||||
[Tooltip("Maximum repeat count (0 = unlimited)")]
|
||||
[Min(0)]
|
||||
public int maxRepeatCount = 20;
|
||||
|
||||
[Tooltip("Fixed repeat count (used when useAmountBasedRepeat is false)")]
|
||||
[Min(1)]
|
||||
public int fixedRepeatCount = 1;
|
||||
|
||||
[Tooltip("Delay between each repeat in seconds (0 for immediate)")]
|
||||
[Min(0)]
|
||||
public float repeatDelay = 0f;
|
||||
public float repeatDelay = 0.15f;
|
||||
|
||||
[Header("Event")]
|
||||
[Tooltip("Unity event to trigger when donation amount is in range")]
|
||||
@ -73,6 +88,29 @@ namespace WefLab
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate repeat count based on donation amount
|
||||
/// </summary>
|
||||
public int GetRepeatCount(int amount)
|
||||
{
|
||||
if (!useAmountBasedRepeat)
|
||||
{
|
||||
return fixedRepeatCount;
|
||||
}
|
||||
|
||||
// Calculate repeat count based on amount
|
||||
int count = amount / amountPerRepeat;
|
||||
|
||||
// Apply min/max limits
|
||||
count = Mathf.Max(count, minRepeatCount);
|
||||
if (maxRepeatCount > 0)
|
||||
{
|
||||
count = Mathf.Min(count, maxRepeatCount);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -89,6 +127,27 @@ namespace WefLab
|
||||
[Tooltip("Donation event triggers based on amount ranges")]
|
||||
public DonationEventTrigger[] donationTriggers = Array.Empty<DonationEventTrigger>();
|
||||
|
||||
[Header("Queue Settings")]
|
||||
[Tooltip("Enable sequential processing of donations")]
|
||||
public bool enableQueue = true;
|
||||
|
||||
[Tooltip("Delay between each donation alert (seconds)")]
|
||||
[Min(0.1f)]
|
||||
public float alertDelay = 3f;
|
||||
|
||||
[Tooltip("Maximum queue size (0 = unlimited)")]
|
||||
[Min(0)]
|
||||
public int maxQueueSize = 50;
|
||||
|
||||
// Queue state (visible in Inspector for debugging)
|
||||
[Header("Queue Status (Read Only)")]
|
||||
[SerializeField] private int queueCount = 0;
|
||||
[SerializeField] private bool isProcessingQueue = false;
|
||||
|
||||
// Donation queue
|
||||
private Queue<DonationData> donationQueue = new Queue<DonationData>();
|
||||
private Coroutine queueProcessorCoroutine = null;
|
||||
|
||||
// Hidden connection info
|
||||
[HideInInspector] public bool isConnected = false;
|
||||
[HideInInspector] public string currentSid = "";
|
||||
@ -532,7 +591,7 @@ namespace WefLab
|
||||
timestamp = timestamp
|
||||
};
|
||||
|
||||
TriggerDonationEvents(donation);
|
||||
EnqueueDonation(donation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -580,8 +639,64 @@ namespace WefLab
|
||||
|
||||
Debug.Log($"[WefLab] Donation: {donorName} - {rawAmount} → {amount} KRW ({platform})");
|
||||
|
||||
// Enqueue donation for sequential processing
|
||||
EnqueueDonation(donation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue donation for sequential processing
|
||||
/// </summary>
|
||||
private void EnqueueDonation(DonationData donation)
|
||||
{
|
||||
if (enableQueue)
|
||||
{
|
||||
// Check queue size limit
|
||||
if (maxQueueSize > 0 && donationQueue.Count >= maxQueueSize)
|
||||
{
|
||||
Debug.LogWarning($"[WefLab] Queue full ({maxQueueSize}), dropping oldest donation");
|
||||
donationQueue.Dequeue();
|
||||
}
|
||||
|
||||
donationQueue.Enqueue(donation);
|
||||
queueCount = donationQueue.Count;
|
||||
Debug.Log($"[WefLab] Donation queued. Queue size: {queueCount}");
|
||||
|
||||
// Start queue processor if not running
|
||||
if (!isProcessingQueue)
|
||||
{
|
||||
queueProcessorCoroutine = StartCoroutine(ProcessDonationQueue());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct trigger without queue (original behavior)
|
||||
TriggerDonationEvents(donation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process donation queue sequentially with fixed delay
|
||||
/// </summary>
|
||||
private IEnumerator ProcessDonationQueue()
|
||||
{
|
||||
isProcessingQueue = true;
|
||||
|
||||
while (donationQueue.Count > 0)
|
||||
{
|
||||
var donation = donationQueue.Dequeue();
|
||||
queueCount = donationQueue.Count;
|
||||
|
||||
// Trigger events
|
||||
TriggerDonationEvents(donation);
|
||||
|
||||
Debug.Log($"[WefLab] Alert triggered. Waiting {alertDelay:F1}s. Remaining in queue: {donationQueue.Count}");
|
||||
|
||||
// Wait for fixed delay
|
||||
yield return new WaitForSeconds(alertDelay);
|
||||
}
|
||||
|
||||
isProcessingQueue = false;
|
||||
queueProcessorCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -606,10 +721,11 @@ namespace WefLab
|
||||
// Check if donation amount is in range
|
||||
if (trigger.IsInRange(donation.amount))
|
||||
{
|
||||
Debug.Log($"[WefLab] Triggering: {trigger.triggerName} (Amount: {donation.amount}, Range: {trigger.minAmount}-{(trigger.maxAmount >= 0 ? trigger.maxAmount.ToString() : "unlimited")}, Repeat: {trigger.repeatCount}x)");
|
||||
int repeatCount = trigger.GetRepeatCount(donation.amount);
|
||||
Debug.Log($"[WefLab] Triggering: {trigger.triggerName} (Amount: {donation.amount}, Range: {trigger.minAmount}-{(trigger.maxAmount >= 0 ? trigger.maxAmount.ToString() : "unlimited")}, Repeat: {repeatCount}x)");
|
||||
|
||||
// Start coroutine to handle repeated invocation
|
||||
StartCoroutine(InvokeRepeatedEvent(trigger, donation));
|
||||
StartCoroutine(InvokeRepeatedEvent(trigger, donation, repeatCount));
|
||||
triggeredAny = true;
|
||||
}
|
||||
}
|
||||
@ -623,15 +739,15 @@ namespace WefLab
|
||||
/// <summary>
|
||||
/// Coroutine to invoke event multiple times with delay
|
||||
/// </summary>
|
||||
private IEnumerator InvokeRepeatedEvent(DonationEventTrigger trigger, DonationData donation)
|
||||
private IEnumerator InvokeRepeatedEvent(DonationEventTrigger trigger, DonationData donation, int repeatCount)
|
||||
{
|
||||
for (int i = 0; i < trigger.repeatCount; i++)
|
||||
for (int i = 0; i < repeatCount; i++)
|
||||
{
|
||||
// Invoke the event
|
||||
trigger.onDonation?.Invoke(donation);
|
||||
|
||||
// Wait for delay before next repetition (skip on last iteration)
|
||||
if (i < trigger.repeatCount - 1 && trigger.repeatDelay > 0)
|
||||
if (i < repeatCount - 1 && trigger.repeatDelay > 0)
|
||||
{
|
||||
yield return new WaitForSeconds(trigger.repeatDelay);
|
||||
}
|
||||
@ -724,6 +840,81 @@ namespace WefLab
|
||||
return isConnected && ws != null && ws.IsAlive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get current queue count
|
||||
/// </summary>
|
||||
public int GetQueueCount()
|
||||
{
|
||||
return donationQueue.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if queue is being processed
|
||||
/// </summary>
|
||||
public bool IsQueueProcessing()
|
||||
{
|
||||
return isProcessingQueue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all pending donations in queue
|
||||
/// </summary>
|
||||
public void ClearQueue()
|
||||
{
|
||||
donationQueue.Clear();
|
||||
queueCount = 0;
|
||||
Debug.Log("[WefLab] Queue cleared");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip current alert and move to next in queue
|
||||
/// </summary>
|
||||
public void SkipCurrentAlert()
|
||||
{
|
||||
if (queueProcessorCoroutine != null)
|
||||
{
|
||||
StopCoroutine(queueProcessorCoroutine);
|
||||
queueProcessorCoroutine = null;
|
||||
}
|
||||
|
||||
if (donationQueue.Count > 0)
|
||||
{
|
||||
Debug.Log("[WefLab] Skipping to next alert");
|
||||
queueProcessorCoroutine = StartCoroutine(ProcessDonationQueue());
|
||||
}
|
||||
else
|
||||
{
|
||||
isProcessingQueue = false;
|
||||
Debug.Log("[WefLab] No more alerts in queue");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pause queue processing
|
||||
/// </summary>
|
||||
public void PauseQueue()
|
||||
{
|
||||
if (queueProcessorCoroutine != null)
|
||||
{
|
||||
StopCoroutine(queueProcessorCoroutine);
|
||||
queueProcessorCoroutine = null;
|
||||
isProcessingQueue = false;
|
||||
Debug.Log("[WefLab] Queue paused");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume queue processing
|
||||
/// </summary>
|
||||
public void ResumeQueue()
|
||||
{
|
||||
if (!isProcessingQueue && donationQueue.Count > 0)
|
||||
{
|
||||
queueProcessorCoroutine = StartCoroutine(ProcessDonationQueue());
|
||||
Debug.Log("[WefLab] Queue resumed");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user