Add mileage pickups and duty free concept
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
@@ -0,0 +1,100 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c5d2e13a8f2417bb6a94e31c8d427f5
|
||||||
|
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: 0
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 1
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 80
|
||||||
|
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: 1
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 1
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
spritePackingTag:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
pSDShowRemoveMatteOption: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -451,6 +451,10 @@ MonoBehaviour:
|
|||||||
- {fileID: 1166831543635676}
|
- {fileID: 1166831543635676}
|
||||||
- {fileID: 1987654321098765}
|
- {fileID: 1987654321098765}
|
||||||
emptyPatternWeight: 2
|
emptyPatternWeight: 2
|
||||||
|
mileageSprite: {fileID: 21300000, guid: 8b2f1d0c3e4a5b6f9d7e0123456789ab, type: 3}
|
||||||
|
mileagePatternChance: 50
|
||||||
|
smallMileageValue: 10
|
||||||
|
bonusMileageValue: 30
|
||||||
--- !u!1 &1832961231703714
|
--- !u!1 &1832961231703714
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -13,8 +13,16 @@ public class GameManager : MonoBehaviour {
|
|||||||
|
|
||||||
private int score = 0; // 게임 점수
|
private int score = 0; // 게임 점수
|
||||||
private int currentLife; // 현재 남은 목숨
|
private int currentLife; // 현재 남은 목숨
|
||||||
|
private int stageMileage = 0; // 현재 플레이 중 획득한 마일리지
|
||||||
|
private int totalMileage = 0; // 누적 보유 마일리지
|
||||||
|
|
||||||
public int maxLife = 3; // 시작 목숨
|
public int maxLife = 3; // 시작 목숨
|
||||||
|
public int initialMileage = 500; // 첫 시작시 지급할 초기 마일리지
|
||||||
|
public bool grantInitialMileage = true; // 초기 마일리지 지급 여부
|
||||||
|
public bool saveMileageImmediately = true; // 스테이지 정산 전까지는 획득 즉시 누적한다.
|
||||||
|
|
||||||
|
private const string TotalMileageKey = "UniRun.TotalMileage";
|
||||||
|
private const string InitialMileageGrantedKey = "UniRun.InitialMileageGranted";
|
||||||
|
|
||||||
// 게임의 스피드를 올리는 변수
|
// 게임의 스피드를 올리는 변수
|
||||||
public float gameSpeed = 1f;
|
public float gameSpeed = 1f;
|
||||||
@@ -44,6 +52,16 @@ public class GameManager : MonoBehaviour {
|
|||||||
|
|
||||||
private void Start() {
|
private void Start() {
|
||||||
currentLife = maxLife;
|
currentLife = maxLife;
|
||||||
|
totalMileage = PlayerPrefs.GetInt(TotalMileageKey, 0);
|
||||||
|
|
||||||
|
if(grantInitialMileage && PlayerPrefs.GetInt(InitialMileageGrantedKey, 0) == 0)
|
||||||
|
{
|
||||||
|
totalMileage += initialMileage;
|
||||||
|
PlayerPrefs.SetInt(TotalMileageKey, totalMileage);
|
||||||
|
PlayerPrefs.SetInt(InitialMileageGrantedKey, 1);
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
UpdateScoreUI();
|
UpdateScoreUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +93,26 @@ public class GameManager : MonoBehaviour {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 마일리지 토큰을 먹었을 때 호출되는 메서드
|
||||||
|
public void AddMileage(int amount) {
|
||||||
|
if(isGameover)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stageMileage += amount;
|
||||||
|
score += Mathf.Max(1, amount / 10);
|
||||||
|
|
||||||
|
if(saveMileageImmediately)
|
||||||
|
{
|
||||||
|
totalMileage += amount;
|
||||||
|
PlayerPrefs.SetInt(TotalMileageKey, totalMileage);
|
||||||
|
PlayerPrefs.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateScoreUI();
|
||||||
|
}
|
||||||
|
|
||||||
// 플레이어가 장애물에 부딪혔을 때 목숨과 속도를 줄이는 메서드
|
// 플레이어가 장애물에 부딪혔을 때 목숨과 속도를 줄이는 메서드
|
||||||
public bool TakeDamage(int damage = 1) {
|
public bool TakeDamage(int damage = 1) {
|
||||||
if(isGameover)
|
if(isGameover)
|
||||||
@@ -100,6 +138,10 @@ public class GameManager : MonoBehaviour {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateScoreUI() {
|
private void UpdateScoreUI() {
|
||||||
scoreText.text = "Score : " + score + "\nLife : " + currentLife + "/" + maxLife + "\nSpeed : " + gameSpeed.ToString("0.0");
|
scoreText.text = "Score : " + score
|
||||||
|
+ "\nLife : " + currentLife + "/" + maxLife
|
||||||
|
+ "\nMileage : " + stageMileage
|
||||||
|
+ "\nTotal : " + totalMileage
|
||||||
|
+ "\nSpeed : " + gameSpeed.ToString("0.0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// 플레이어가 먹을 수 있는 마일리지 토큰
|
||||||
|
public class MileagePickup : MonoBehaviour
|
||||||
|
{
|
||||||
|
public int mileageValue = 10; // 획득 마일리지
|
||||||
|
|
||||||
|
private bool collected = false; // 이미 획득했는지 여부
|
||||||
|
private SpriteRenderer spriteRenderer;
|
||||||
|
private Collider2D pickupCollider;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
spriteRenderer = GetComponent<SpriteRenderer>();
|
||||||
|
pickupCollider = GetComponent<Collider2D>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Setup(Sprite sprite, int value)
|
||||||
|
{
|
||||||
|
mileageValue = value;
|
||||||
|
|
||||||
|
if(spriteRenderer != null)
|
||||||
|
{
|
||||||
|
spriteRenderer.sprite = sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetPickup()
|
||||||
|
{
|
||||||
|
collected = false;
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
|
||||||
|
if(pickupCollider != null)
|
||||||
|
{
|
||||||
|
pickupCollider.enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTriggerEnter2D(Collider2D other)
|
||||||
|
{
|
||||||
|
if(collected || !other.CompareTag("Player"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
collected = true;
|
||||||
|
|
||||||
|
if(GameManager.instance != null)
|
||||||
|
{
|
||||||
|
GameManager.instance.AddMileage(mileageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d68f1d3b0a9e4aef9b0a6d8f9c2e1a44
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+140
-2
@@ -1,34 +1,49 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
// 발판으로서 필요한 동작을 담은 스크립트
|
// 발판으로서 필요한 동작을 담은 스크립트
|
||||||
public class Platform : MonoBehaviour {
|
public class Platform : MonoBehaviour {
|
||||||
public GameObject[] obstacles; // 장애물 오브젝트들
|
public GameObject[] obstacles; // 장애물 오브젝트들
|
||||||
public int emptyPatternWeight = 2; // 장애물이 없는 패턴이 선택될 가중치
|
public int emptyPatternWeight = 2; // 장애물이 없는 패턴이 선택될 가중치
|
||||||
|
public Sprite mileageSprite; // 이 발판에서 사용할 마일리지 토큰 스프라이트
|
||||||
|
public int mileagePatternChance = 50; // 마일리지 패턴 등장 확률
|
||||||
|
public int smallMileageValue = 10; // 일반 토큰 가치
|
||||||
|
public int bonusMileageValue = 30; // 어려운 루트 보너스 토큰 가치
|
||||||
|
|
||||||
private bool stepped = false; // 플레이어 캐릭터가 밟았었는가
|
private bool stepped = false; // 플레이어 캐릭터가 밟았었는가
|
||||||
|
private MileagePickup[] mileagePickups; // 런타임에 생성하는 마일리지 토큰 풀
|
||||||
|
private const int MileagePoolSize = 5;
|
||||||
|
|
||||||
// 컴포넌트가 활성화될때 마다 매번 실행되는 메서드
|
// 컴포넌트가 활성화될때 마다 매번 실행되는 메서드
|
||||||
private void OnEnable() {
|
private void OnEnable() {
|
||||||
// 발판을 리셋하는 처리
|
// 발판을 리셋하는 처리
|
||||||
|
|
||||||
stepped = false;
|
stepped = false;
|
||||||
|
EnsureMileagePickups();
|
||||||
|
|
||||||
for(int i = 0; i < obstacles.Length; i++)
|
for(int i = 0; i < obstacles.Length; i++)
|
||||||
{
|
{
|
||||||
obstacles[i].SetActive(false);
|
obstacles[i].SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HideMileagePickups();
|
||||||
|
|
||||||
if(obstacles.Length == 0)
|
if(obstacles.Length == 0)
|
||||||
{
|
{
|
||||||
|
TryPlaceMileagePattern(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 낮은 장애물과 높은 장애물이 동시에 켜져 불가능한 배치가 되지 않도록 하나만 선택한다.
|
// 낮은 장애물과 높은 장애물이 동시에 켜져 불가능한 배치가 되지 않도록 하나만 선택한다.
|
||||||
int patternIndex = Random.Range(0, obstacles.Length + emptyPatternWeight);
|
int patternIndex = Random.Range(0, obstacles.Length + emptyPatternWeight);
|
||||||
|
GameObject activeObstacle = null;
|
||||||
|
|
||||||
if(patternIndex < obstacles.Length)
|
if(patternIndex < obstacles.Length)
|
||||||
{
|
{
|
||||||
obstacles[patternIndex].SetActive(true);
|
activeObstacle = obstacles[patternIndex];
|
||||||
|
activeObstacle.SetActive(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TryPlaceMileagePattern(activeObstacle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnCollisionEnter2D(Collision2D collision) {
|
void OnCollisionEnter2D(Collision2D collision) {
|
||||||
@@ -39,4 +54,127 @@ public class Platform : MonoBehaviour {
|
|||||||
GameManager.instance.AddScore(1);
|
GameManager.instance.AddScore(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureMileagePickups() {
|
||||||
|
if(mileagePickups != null && mileagePickups.Length == MileagePoolSize)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mileagePickups = new MileagePickup[MileagePoolSize];
|
||||||
|
|
||||||
|
for(int i = 0; i < MileagePoolSize; i++)
|
||||||
|
{
|
||||||
|
GameObject pickupObject = new GameObject("Mileage Pickup " + (i + 1));
|
||||||
|
pickupObject.transform.SetParent(transform);
|
||||||
|
pickupObject.transform.localScale = Vector3.one;
|
||||||
|
|
||||||
|
SpriteRenderer spriteRenderer = pickupObject.AddComponent<SpriteRenderer>();
|
||||||
|
spriteRenderer.sprite = mileageSprite;
|
||||||
|
spriteRenderer.sortingLayerName = "Foreground";
|
||||||
|
spriteRenderer.sortingOrder = 1;
|
||||||
|
|
||||||
|
CircleCollider2D pickupCollider = pickupObject.AddComponent<CircleCollider2D>();
|
||||||
|
pickupCollider.isTrigger = true;
|
||||||
|
pickupCollider.radius = 0.35f;
|
||||||
|
|
||||||
|
mileagePickups[i] = pickupObject.AddComponent<MileagePickup>();
|
||||||
|
mileagePickups[i].Setup(mileageSprite, smallMileageValue);
|
||||||
|
|
||||||
|
pickupObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideMileagePickups() {
|
||||||
|
if(mileagePickups == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < mileagePickups.Length; i++)
|
||||||
|
{
|
||||||
|
if(mileagePickups[i] != null)
|
||||||
|
{
|
||||||
|
mileagePickups[i].gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryPlaceMileagePattern(GameObject activeObstacle) {
|
||||||
|
if(mileageSprite == null || mileagePickups == null || Random.Range(0, 100) >= mileagePatternChance)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activeObstacle == null)
|
||||||
|
{
|
||||||
|
PlaceSafeMileageLine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activeObstacle.name.Contains("Slide"))
|
||||||
|
{
|
||||||
|
PlaceSlideMileageLine();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlaceJumpMileageArc(activeObstacle.transform.localPosition.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlaceSafeMileageLine() {
|
||||||
|
Vector2[] positions = {
|
||||||
|
new Vector2(-2f, 2.25f),
|
||||||
|
new Vector2(-1f, 2.25f),
|
||||||
|
new Vector2(0f, 2.25f),
|
||||||
|
new Vector2(1f, 2.25f),
|
||||||
|
new Vector2(2f, 2.25f)
|
||||||
|
};
|
||||||
|
|
||||||
|
PlaceMileageTokens(positions, smallMileageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlaceJumpMileageArc(float obstacleX) {
|
||||||
|
Vector2[] positions = {
|
||||||
|
new Vector2(obstacleX - 1.2f, 2.7f),
|
||||||
|
new Vector2(obstacleX, 3.45f),
|
||||||
|
new Vector2(obstacleX + 1.2f, 2.7f),
|
||||||
|
new Vector2(obstacleX + 1.9f, 2.25f)
|
||||||
|
};
|
||||||
|
|
||||||
|
PlaceMileageTokens(positions, smallMileageValue);
|
||||||
|
SetPickupValue(1, bonusMileageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlaceSlideMileageLine() {
|
||||||
|
Vector2[] positions = {
|
||||||
|
new Vector2(-1.5f, 1.45f),
|
||||||
|
new Vector2(-0.5f, 1.45f),
|
||||||
|
new Vector2(0.5f, 1.45f),
|
||||||
|
new Vector2(1.5f, 1.45f)
|
||||||
|
};
|
||||||
|
|
||||||
|
PlaceMileageTokens(positions, smallMileageValue);
|
||||||
|
SetPickupValue(2, bonusMileageValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlaceMileageTokens(Vector2[] positions, int value) {
|
||||||
|
int count = Mathf.Min(positions.Length, mileagePickups.Length);
|
||||||
|
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mileagePickups[i].Setup(mileageSprite, value);
|
||||||
|
mileagePickups[i].transform.localPosition = positions[i];
|
||||||
|
mileagePickups[i].ResetPickup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPickupValue(int index, int value) {
|
||||||
|
if(index < 0 || index >= mileagePickups.Length || !mileagePickups[index].gameObject.activeSelf)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mileagePickups[index].Setup(mileageSprite, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,3 +311,22 @@ UI 아이콘은 텍스트 없이 의미가 읽히는 심볼 중심으로 사용
|
|||||||
|
|
||||||
- `Assets/ConceptArt/stage_background_concept_01.png`
|
- `Assets/ConceptArt/stage_background_concept_01.png`
|
||||||
- `Assets/ConceptArt/stage_background_panels_preview.png`
|
- `Assets/ConceptArt/stage_background_panels_preview.png`
|
||||||
|
|
||||||
|
## 채택한 면세점 화면 콘셉트
|
||||||
|
|
||||||
|
면세점은 스테이지 사이에서 공용 마일리지로 준비 아이템을 구매하는 화면이다. 화면의 중심은 상품 진열과 구매이며, 다음 맵 이동은 오른쪽 표지판으로만 안내한다.
|
||||||
|
|
||||||
|
화면 구성:
|
||||||
|
|
||||||
|
- 왼쪽/중앙: 도시락, 운동화, 가방, 방어막 같은 구매 상품 진열
|
||||||
|
- 상단: 보유 공용 마일리지 표시
|
||||||
|
- 오른쪽: 비행기 아이콘과 오른쪽 방향성을 가진 다음 맵 이동 표지판
|
||||||
|
- 왼쪽 표지판은 배치하지 않는다.
|
||||||
|
- 큰 비행기 자체를 화면 주인공으로 두지 않고, 비행기는 표지판 아이콘으로만 사용한다.
|
||||||
|
- 상품 가격은 지역 화폐가 아니라 공용 마일리지 하나로 통일한다.
|
||||||
|
- 도시락은 목숨 +1 회복이 바로 읽히도록 하트와 +1 느낌을 사용한다.
|
||||||
|
- 가방은 기본 1칸에서 최대 3칸까지 확장되는 아이템 슬롯 업그레이드로 표현한다.
|
||||||
|
|
||||||
|
컨셉/검토 파일:
|
||||||
|
|
||||||
|
- `Assets/ConceptArt/duty_free_shop_screen_concept_01.png`
|
||||||
|
|||||||
Reference in New Issue
Block a user