https://bbiguduk.gitbook.io/swift/

 

The Swift Programming Language (한국어) - Swift

The Swift Programming Language (한국어)

bbiguduk.gitbook.io

Swift 공식문서 한글화 사이트

 

 

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/revisionhistory/

 

Documentation

 

docs.swift.org

https://github.com/apple/swift-evolution

 

GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Lang

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhance...

github.com

Swift의 변화과정들을  볼수있는 사이트

 

var x : Int = 10;

let y : Int = 20;
y = 30;
//error: cannot assign to value: 'y' is a 'let' constant

var z = 3;

print(x)
print(y)
print(z)

//main.swift:8:12: error: '=' must have consistent whitespace on both sides

let은 상수를 선언할때 사용되며

위와 같이 초깃값이 있을 경우에는 컴파일러가 타입 추론(type inference)을 하므로 데이터 타입을 명시할 필요 없다

 

print(1.0, 2.0, 3.0, 4.0, 5.0, separator: " ... ") // Prints "1.0 ... 2.0 ... 3.0 ... 4.0 ... 5.0"

for n in 1...5 { print(n, terminator: "") } // Prints "12345"

 

var x = 10
print(type(of:x))
let s = MemoryLayout.size(ofValue: x)//8
let t = MemoryLayout<Int>.size
print(s, t)

//Int
//8 8

swift 에서는 Int형의 크기가 8byte 이다

데이터타입의 크기들은 플랫폼에 따라 다르다. Int형을 예시로 들자면 대부분의 언어와 컴파일러에선 4Byte로 인식하여 사용하지만 Swift 같은 경우 Int형의 크기를 8Byte로 사용한다.

 

var x : Int = 10
print(x)
print("x")
print("\(x)")
print("값은 \(x)입니다.")
print("Int32 Min = \(Int32.min) Int32 Max = \(Int32.max)")


10
x
10
값은 10입니다.
Int32 Min = -2147483648 Int32 Max = 2147483647

 

원하는 값을 문자열로 출력하기 위해선 \( ) 를 사용한다

 

var userName : String = "Kim" // : String 생략하는 것이 일반적임
var age = 20
var message = "\(userName)의 나이는 \(age)입니다."
print(message) // Kim의 나이는 20입니다.

문자열 보간(string interpolation)을 사용하여 문자열과 변수, 상수, 표현식, 함수 호출의 조합으로 만들 수도 있다

 

타입 어노테이션(type annotation)

 Int 타입의 userCount 라는 이름의 변수를 선언

 var userCount : Int = 10 // : Int가 type annotation

 

선언부에 타입 어노테이션이 없으면 스위프트 컴파일러는 상수 또는 변수의 타입을 식별하기 위하여 타입 추론(type inference) 사용

 해당 상수 또는 변수에 값이 할당되는 시점에서 그 값의 타입을 확인하고 그와 같은 타입처럼 사용

 var signalStrength = 2.231 // var signalStrength : Double = 2.231

 let companyName = "My Company"

 signalStrength라는 변수를 Double 타입(스위프트의타입 추론에서 모든 부동 소수점 수는 Double 타입)

 companyName이라는 상수는 String 타입으로 간주

 

 

상수를 선언할 때도 타입 어노테이션을 사용하면 나중에 코드에서 값을 할당할 수 있다.

let bookTitle: String 
var book = true
if book { 
bookTitle = "iOS" 
}
else { 
bookTitle = "Android" 
} 
print(bookTitle)

 상수에는 값을 한 번만 할당할 수 있다

 

튜플은 Swift에서 가장 강력한 기능 중 하나이며 여러 값을 하나의 개체에 일시적으로 묶는 방법이다.

튜플에 저장되는 항목들은 어떠한 타입도 될 수 있으며, 저장된 값들이 모두 동일한 타입일 필요도 없다

let myTuple = (10, 12.1, "Hi")
var myString = myTuple.2
print(myString)
print(myTuple.1,myTuple.2,myTuple.0)

 

튜플을 생성할 때 각 값에 이름을 할당할 수도 있다

let myTuple = (count: 10, length: 12.1, message: "Hi")
let (myInt, myFloat, myString) = myTuple
print(type(of:myTuple))
print(myTuple.0, myTuple.count)

//(count: Int, length: Double, message: String)
//10 10

 

Void는 빈 튜플(empty tuple) 이다

typealias Void = () //아무 내용도 없는 튜플,

typealias name = existing_type

 

Optional(옵셔널)은 값이 존재하지 않을 수 있는 상황을 다루기 위한 개념으로 다양한 프로그래밍 언어에서 지원됩니다.

var x : Int = 10
var y : Int?
var z : Int!
print(x,y,z)
//10 nil nil
var x : Int = 10
var y : Int? = 5

print(x,y)
//10 Optional(5)

옵셔널 타입 변수를 선언하기 위해서는 타입 선언부 뒤에 “?” 문자를 쓴다

var index: Int?

- index 변수는 정수 값을 갖거나 아무 값도 갖지 않을 수 있다(nil)

'iOS' 카테고리의 다른 글

iOS (?)  (0) 2023.11.14
iOS (10)  (0) 2023.11.07
iOS (5)  (1) 2023.10.17
iOS (3)  (1) 2023.09.26
iOS (2)  (0) 2023.09.19

-샘플무기 추가

-샘플무기 변경

-샘플무기 발사

https://steamcommunity.com/sharedfiles/filedetails/?id=1629158033

샘플로 사용할 이미지를 가져왔지만 써도되는지 모르지만 그냥 썻다

 

먼저 무기를 추가하기위한 무기데이터를 만들어줍시다

[CreateAssetMenu(fileName ="MonsterData ", menuName ="Pakuri/New Monster Data", order =2)]
public class MonsterData : ScriptableObject
{
    public string m_name = string.Empty;

    public GameObject m_obj = null;

    public float m_speed = 0.0f;

    public Vector2Int m_damage = Vector2Int.zero;

    public float IsDamage
    {
        get
        {
            return Random.Range(m_damage.x, m_damage.y + 1);
        }
    }
}

에셋메뉴를 추가해주었습니다

 

위 사진과 같이 생성한 변수를 바탕으로 데이터를 입력할 수 있지

 

public class GManager : MonoBehaviour
{
    public List<WeaponData> m_weaponList = null;
    Dictionary<string, WeaponData> m_weaponDic = null;


    public static GManager Instance
    {
        get
        {
            return g_instance;
        }
    }
    private void Awake()
    {
        if (GManager.Instance == null)
        {
            g_instance = this;
            WeaponSetting();
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    void WeaponSetting()
    {
        m_weaponDic = new Dictionary<string, WeaponData>();
        for (int i=0; i<m_weaponList.Count; i++)
        {
            m_weaponDic.Add(m_weaponList[i].m_weaponName, m_weaponList[i]);
        }
        m_weaponList.Clear();
    }

    /// <summary>
    /// 오브젝트 생성 배치
    /// </summary>
    /// <param name="argObj">생성할 오브젝트</param>
    /// <param name="argParentT">부모 오브젝트</param>
    /// <param name="argParentFlag">참: 부모관계형성  거짓: 이미 부모관계</param>
    /// <returns>생성된 오브젝트</returns>
    public GameObject CreatObj(GameObject argObj, Transform argParentT, bool argParentFlag = true)
    {
        GameObject _obj = null;
        switch (argParentFlag)
        {
            case true:
                _obj = Instantiate(argObj, argParentT);
                _obj.transform.localPosition = Vector3.zero;
                _obj.transform.localRotation = Quaternion.identity;
                _obj.name = "ViewObj";
                break;
            case false:
                _obj = Instantiate(argObj);
                _obj.transform.localPosition = argParentT.position;
                _obj.transform.localRotation = argParentT.rotation;
                _obj.name = "ViewObj";
                break;

        }
        return _obj;
    }

    /// <summary>
    /// 무기 반환
    /// </summary>
    /// <param name="argStr">무기 이름</param>
    /// <returns>무기 데이터</returns>
    public WeaponData GetWeaponData(string argStr)
    {
        return m_weaponDic.ContainsKey(argStr) ? m_weaponDic[argStr] : null;
 
    }
    
}

CreatObj :

오브젝트를 생성해줍니다. 무기뿐만 아닌 다양한 곳에서 사용될 것입니다.

 

WeaponSetting:

무기데이터의 이름값을 키값으로 사용하는 딕셔너리를 생성하고 초기화 해줬습니다

 

GetWeaponData:

무기데이터를 반환해줍니다

 

무기에 빈 오브젝트를 추가해줬습니다. 총알이 발사될 위치가 될것이야

위의 무기는 샷건이기에 총알이 발사될 위치를 5개로 설정해주었습니다.

 

public class Weapon : MonoBehaviour
{
    /// <summary>
    /// 총알발사 베이스 트랜스폼
    /// </summary>
    public Transform[] m_sBaseT = null;

    /// <summary>
    /// 스프라이트 렌더러
    /// </summary>
    public SpriteRenderer m_spRenderer = null;

    void Start()
    {
        
    }


    private void LateUpdate()
    {
        Vector2 _pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        m_spRenderer.flipY = transform.position.x < _pos.x ? false : true;
    }
}

무기에 들어갈 스크립트

 

public class Bullet : MonoBehaviour
{
    public float m_speed = 0.0f;

    public bool m_nonCallFlag = false;

    Vector2 m_startPos = Vector2.zero;

    float m_atkLength = 0.0f;

    public void bulletTimeSetting(float argAtkLength)
    {
        m_startPos = transform.position;
        m_atkLength = argAtkLength;
        Destroy(gameObject, 4.0f);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(m_speed * Time.deltaTime * Vector2.right);
        if(!GManager.Instance.CheckLength(m_startPos, transform.position, m_atkLength))
        {
            Destroy(gameObject);
        }
    }
}

총알 스크립트

적당한 이미지를 구하고 스크립트 부착후 prefab으로 만들어주었습니다

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public WeaponType.Type m_weaponType = WeaponType.Type.Rifle;

    public float m_speed = 0.0f;


    Transform m_weaponBase = null;
    string m_weaponName = string.Empty;
    GameObject m_viewObj = null;
    GameObject m_weaponObj = null;
    WeaponData m_wData = null;

    List<Transform> m_shotBaseT = new List<Transform>();

    bool m_atkFlag = false;

  
    void Start()
    {
        startSetting();
    }
    
    void startSetting()
    {
        m_weaponBase = transform.Find("WeaponBase");
        WeaponList();
        LoadWeapon(m_weaponName);
    }

    // Update is called once per frame
    void Update()
    {
        AttackController();
    }
    void WeaponList()
    {
        switch (m_weaponType)
        {
            case WeaponType.Type.Rifle:
                m_weaponName = "Rifle";
                break;
            case WeaponType.Type.Carbin:
                m_weaponName = "Carbin";
                break;
            case WeaponType.Type.LMG:
                m_weaponName = "LMG";
                break;
            case WeaponType.Type.Shotgun:
                m_weaponName = "Shotgun";
                break;
        }
    }
    void LoadWeapon(string argName)
    {
        if(m_weaponObj !=null)
        {
            m_wData = null;
            m_shotBaseT.Clear();
            Destroy(m_weaponObj);
        }

        m_wData = GManager.Instance.GetWeaponData(argName);
        //Debug.Log("생");
        if (m_wData == null) return;
        m_weaponObj = GManager.Instance.CreatObj(m_wData.m_weaponObj, m_weaponBase);
        //Debug.Log("성");
        Weapon _weaponBaseSc = m_weaponObj.GetComponent<Weapon>();
        for(int i = 0; i< _weaponBaseSc.m_sBaseT.Length; i++)
        {
            m_shotBaseT.Add(_weaponBaseSc.m_sBaseT[i]);
        }

    }
    
    void AttackController()
    {
        if (m_atkFlag) return;
        if(Input.GetAxis("Fire1") > 0.0f)
        {
            m_atkFlag = true;
            for(int i=0; i<m_shotBaseT.Count; i++)
            {
                Quaternion _rot = m_shotBaseT[i].localRotation;
                m_shotBaseT[i].Rotate(0.0f, 0.0f, Random.Range(-m_wData.m_dispersionAngle, m_wData.m_dispersionAngle));
                GameObject _bulletObj = GManager.Instance.CreatObj(m_wData.m_bulletObj, m_shotBaseT[i], false);
                _bulletObj.GetComponent<Bullet>().bulletTimeSetting(m_wData.m_atkLength);
                m_shotBaseT[i].localRotation = _rot;
            }
            Invoke("ClearAtk", m_wData.m_reAtkTime);
        }
    }

    void ClearAtk()
    {
        m_atkFlag = false;
    }
}

테스트 과정중 무기 선택을 쉽게하기 위해서 열거형 타입변수 추가해주고

WeaponList로 선택할 수 있게 만들어주었습니다

 

Loadweapon:

선택한 무기데이터를 불러오고 그에 맞는 오브젝트를 생성해줍니다

 

AttackController

무기발사를 위한 것입니다. m_dispersionAngle은 흔들림 각도입니다.

값이 높을수록 "탄퍼짐"이 심해집니다

 

 

======결과물=======

샷건
카빈
라이플
기관총

 

'파쿠리프로젝트' 카테고리의 다른 글

파쿠리 프로젝트(1)  (0) 2022.12.28
파쿠리 프로젝트(0)  (0) 2022.12.28

- 오늘의 할일

- 플레이어 이동

- GManager 생성

- 2D용 LookAt

 

-플레이어 이동

먼저 플레이어 캐릭터로 사용할 임시 이미지를 설정햇다

플레이어 캐릭터

컴퓨터 포맷을 위해 자료를 정리하던중 찾은 캐릭터 PNG이미지를 활용하여 맹글었다.

 

스프라이트 에디터로 슬라이스 해주었다

슬라이스 후 Player오브젝트를 만들고

플레이어 -> 무기생성위치베이스 -> 무기

GManager 이름의 오브젝트도 생성해주었따

 

플레이어에 간단한 이동만 추가주고

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    MoveType.Type m_moveType = MoveType.Type.Idle;

    DirType.Type m_dirType = DirType.Type.Left;

    public float m_speed = 0.0f;

    void Start()
    { 
    }
    
    // Update is called once per frame
    void Update()
    {
        MoveController();
    }

    void MoveController()
    {
        Vector2 _input = Vector2.zero;
        _input.x = Input.GetAxisRaw("Horizontal");
        _input.y = Input.GetAxisRaw("Vertical");

        transform.Translate(m_speed * Time.deltaTime * _input.normalized);
    }
}

 

Gmanager에도 스크립트 부착

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GManager : MonoBehaviour
{
    static GManager g_instance = null;

    public static GManager Instance
    {
        get
        {
            return g_instance;
        }
    }
    private void Awake()
    {
        if (GManager.Instance == null)
        {
            g_instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    /// <summary>
    /// 2d용 LookAt
    /// </summary>
    /// <param name="argStartPos">현재위치</param>
    /// <param name="argTargetPos">처다볼위치</param>
    /// <param name="argFixRot">수정할 회전값</param>
    /// <returns>각도</returns>
    public Quaternion LookAt2D(Vector2 argStartPos, Vector2 argTargetPos, float argFixRot = 0.0f)
    {
        Vector2 _dir = argTargetPos - argStartPos;
        float _angle = Mathf.Atan2(_dir.y, _dir.x) * Mathf.Rad2Deg - argFixRot;

        return Quaternion.AngleAxis(_angle, Vector3.forward);
    }
}

Gmanager의 스크립트: 싱글톤 패턴

천상천하 유아독존

건방진 Gmanager는 단 하나만 존재할 수 있다

 

 

WeaponBase에도 스크립트 부착

public class WeaponBase : MonoBehaviour
{
    private void LateUpdate()
    {
        Vector2 _pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        transform.rotation = GManager.Instance.LookAt2D(transform.position, _pos);
    }
}

GManager 스크립트에서 가져온 LookAt2D

 

 

***결과물***

마우스를 잘 따라가고 잘 움직여진다

 

'파쿠리프로젝트' 카테고리의 다른 글

파쿠리프로젝트(2)  (0) 2022.12.31
파쿠리 프로젝트(0)  (0) 2022.12.28

요즘 재밌게 하고있는 모바일게임 탕탕특공대

스팀에 등록되어있는 뱀파이어 서바이벌(Vampire Survivors)과  상당히 유사한 게임으로 누워서 하기 딱좋다

 

 

유니티에셋 스토어에 등록되어 있는 GUI들도 많이 보이고

유니티 GUI PRO KIT

https://assetstore.unity.com/packages/2d/gui/gui-pro-kit-casual-game-176695

 

GUI PRO Kit - Casual Game | 2D GUI | Unity Asset Store

Elevate your workflow with the GUI PRO Kit - Casual Game asset from Layer Lab. Find this & more GUI on the Unity Asset Store.

assetstore.unity.com

 

물론 만들기 어렵겠지만 뭔가 만만해보이므로

 

탕탕특공대의 파쿠리게임을 만들어 볼것이야

 

'파쿠리프로젝트' 카테고리의 다른 글

파쿠리프로젝트(2)  (0) 2022.12.31
파쿠리 프로젝트(1)  (0) 2022.12.28

2D 캐릭터에 대한 이동조작

void CharacterMove ()
    {
        Vector2 _input = Vector2.zero;
        float _speed = 3.0f;
        float _move = 0.0f;
        float _down = 0.0f;

        _input.x = Input.GetAxisRaw("Horizontal");
        _input.y = Input.GetAxisRaw("Vertical");
        
        float _speed = m_data.m_speed[(int)_down];
         switch (_input)
        {
            case Vector2 v when v.Equals(Vector2.zero):
                break;
            default:
                _move = _input.x == 0.0f ? 0.0f : 1.0f;
                _down = _input.y < 0.0f ? 1.0f : 0.0f;
                if (!m_rollFlag && _move != 0.0f) transform.localScale = Gmanager.Instance.SetFlip(_input, ref m_flipFlag);
                break;
        }
        transform.Translate(_speed * _input.x * Time.deltaTime * Vector2.right);        
    }

변수 _move와 _down은 각각 이동상태 체크와 서있는지 앉아있는지 여부를 체크합니다

상태에 따른 캐릭터의 애니메이션 변경과  캐릭터 방향에 따른 플립에 사용됩니다.

Gmanager클래스는 따로 만들어준 클래스입니다.

 public Vector3 SetFlip(Vector2 argInput, ref bool argFlipflag)
    {// ref <-- C의 포인터 임
        Vector3 _scale = Vector3.one;

        switch ((int)argInput.x)
        {
            case -1:
                argFlipflag = true;
                break;
            case 1:
                argFlipflag = false;
                break;
        }

        _scale.x = argInput.x;

        return _scale;
    }

Gmanager클래스에 포함되어있는 SetFlip함수입니다.

'Unity' 카테고리의 다른 글

[Unity] SerializeField ? 직렬화 ?  (0) 2024.01.22
[Unity C#] Vector3  (0) 2024.01.22
2024-01-10  (0) 2024.01.10
2024_01_08  (0) 2024.01.08
2024-01-04// 옵셔널 체이닝, Rect Transform, Bounds  (0) 2024.01.04

+ Recent posts