1학년 1학기 여름방학 때 동기들과 함께 2022 메타버스 개발자 경진대회에 참가했다.

우리는 360헥사월드를 이용해 직접 랜드마크 건물들을 만들고 유니티로 미래의 서울을 만드는 과제를 맡았다.

 

아래의 영상들이 1학년 5명이 모여 열심히 작업한 결과물을 담은 영상이다.

 

아래의 영상은 우리 팀의 팀장 방모씨가 우리의 결과물을 직접 나레이션하여 제작한 영상이다.

 

 

결과는 1차합격 후 2차에서 떨어졌다. (ㅠㅠ)

1차 합격 당시 매우 기쁜 상태를 보여주는 인스타 스토리다..ㅎㅎ

2차에서 떨어져 무척이나 아쉬웠다. 1차 합격한 후 2차를 위해 더 많은 아이디어를 내고 준비하고 있었던 우리였기 때문이다. 상심이 컸지만 지금 생각하면 정말 좋은 경험이라 생각된다. 

'game > unity(C#)' 카테고리의 다른 글

나만의 리듬게임 만들기 - 3  (0) 2022.09.13
나만의 리듬게임 개발일지 - 2  (0) 2022.08.10
나만의 리듬게임 개발일지 - 1  (0) 2022.07.27
2D 기초 - 3 (Jump 구현)  (0) 2022.07.21
2D 기초 - 2 (Player 이동 구현)  (0) 2022.07.14

베토벤 바이러스로 게임을 만들기 전에 World's Smallest Violin에서 박자감이 좋은 부분을 따와서 간단한 stage를 만들었다.

박자에 맞춰 pointbox를 배치할 때에는 직접 리듬에 맞춰 플레이하고 Debug를 이용해 위치를 확인한 후 배치했다.

 

배치 후 플레이 영상이다.

 

30초 밖에 되지 않는 음악을 리듬 게임으로 만들었지만 느낀 점이 있다.

첫째, 실행할 때 마다 음악 싱크가 제각각이다.

그냥 bgm으로 음악을 실행하는 것이라면 딱히 문제가 되지 않았겠지만 리듬 게임을 만들다보니 크게 체감 되었다. 전체 실행 횟수 중 30퍼센트 가량 싱크가 안맞았기 때문에 계속해서 pointbox의 위치를 수정하다가 흥미를 잃었다.

둘째, 내가 리듬게임을 못하는 것 같다.

pointbox의 위치가 알맞은지, 벽의 폭이 적당한지를 알아보기 위해 테스트를 하는데 쉽지 않았다.

 

pointbox를 먹었을 때의 이펙트, 카메라 회전, 생동감 있는 player와 게임 화면 등을 개선시켜 좀 더 재밌는 게임처럼 만들고자 했으나 여러가지의 이유로 나만의 리듬게임 만들기는 여기까지 하는 것으로 정했다.

먼저 벽과 공(캐릭터)간의 충돌을 감지할 수 있도록 했다.

 

    private void OnCollisionEnter2D(Collision2D other) 
    {
        if (other.gameObject.tag == "Wall")
        {
            Debug.Log("GameOver");
        }
    }

 

간단하게 콘솔창을 통해 충돌을 감지하는지 확인했다.

 


그 후에는 player가 pointbox에서 키를 눌려 점수를 올릴 수 있도록 했다.

 

Player c#스크립트

public class PlayerMove : MonoBehaviour
{   
    public bool isPoint = false;
    public int point = 0;
    public float Speed;
    private int directionIs = 0;
    Rigidbody2D rigid;

    // Start is called before the first frame update
    void Awake()
    {

    }

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

    void FixedUpdate() 
    {
        Move();
    }
    private void Rotation()
    {
        //키입력 시에 방향전환
        if (Input.GetKeyDown(KeyCode.D))
        {
            if (directionIs == 0)
                directionIs = 3;
            else
                directionIs = 0;
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            if (directionIs == 1)
                directionIs = 2;
            else
                directionIs = 1;
        }
        
    }

    private void Move()
    {
        if (directionIs==0)
            transform.position = new Vector2(transform.position.x, transform.position.y + Speed);
        else if (directionIs==1)
            transform.position = new Vector2(transform.position.x + Speed, transform.position.y);
        else if (directionIs==2)
            transform.position = new Vector2(transform.position.x - Speed, transform.position.y);
        else if (directionIs==3)
            transform.position = new Vector2(transform.position.x, transform.position.y - Speed); 
    }

    private void OnCollisionEnter2D(Collision2D other) 
    {
        if (other.gameObject.tag == "Wall")
        {
            Debug.Log("GameOver");
        }
    }

    private void OnTriggerEnter2D(Collider2D other) 
    {
        if (other.gameObject.tag == "PointBox")
        {
            isPoint = true;
        }
    }

    private void Rotate() //부드럽게 만들기
    {
        if (Input.GetMouseButtonDown(0))
        {
            for(int i=1; i<91; i++)
            {
                transform.Rotate(0, 0, i);
            }
        }
    }
    private void OnTriggerExit2D(Collider2D other) 
    {
        if (other.gameObject.name == "PointBox")
        {
            isPoint = false;
        }
    }
}

 

pointbox c#스크립트

public class Point : MonoBehaviour
{
    public GameObject PlayerMove;
    private bool istouch = false;
    void Start()
    {
        
    }

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

    private void Delete()
    {
        if ((Input.GetKeyDown(KeyCode.Space)||Input.GetKeyDown(KeyCode.A)||Input.GetKeyDown(KeyCode.D))&&PlayerMove.GetComponent<PlayerMove>().isPoint&&istouch)
        {
            PlayerMove.GetComponent<PlayerMove>().point++;
            print(PlayerMove.GetComponent<PlayerMove>().point);
            Destroy(this.gameObject);
        }
    }

    private void OnTriggerEnter2D(Collider2D other) 
    {
        if (other.gameObject.name == "Player")
        {
            istouch = true;
        }
    } 

    private void OnTriggerExit2D(Collider2D other) 
    {
        if (other.gameObject.tag == "Player")
        {
            istouch = false;
        }
    }

}

 

'A' 키를 눌러야 할 때에는 빨강색, 'D'는 파랑색으로 지정했다. 

그리고 지난번 방향 전환에서 좀 더 추가해서 'A' 키는 좌우를, 'D'키는 위아래를 움직일 수 있도록 했다.

 

첫번째로 만들 리듬게임의 음악은 '베토벤 바이러스'로 정했다. 그 후 turn 혹은 space바를 눌러야하는 지점에 pointbox를 깔아주었다.

 

Player가 pointbox와 닿았을 때 키를 누르면 pointbox가 사라지도록 하기 위해 OnTriggerEnter2D 함수 안에서 Input을 사용해봤지만 생각대로 작동하지 않았다. 그 이유는 OnTriggerEnter는 1 프레임에만 사용되기 때문이였다. 동일한 프레임(1 프레임에만 해당)에서 키를 눌러야 작동하는 것이었다. 

그래서 bool 변수를 하나 선언해서 trigger 안에 있을 때와 없을 때 각각 OntriggerEnter와 OntriggerExit를 이용해 bool 값을 지정해주었고, pointbox에 적용할 스크립트를 생성해 player에 적용한 스크립트에서 bool 변수를 가져와 pointbox에 닿았을 때 키를 누르면 pointbox가 사라지도록 만들었다.

 

일단 이정도까지 하고 난 뒤 게임을 실행했을 때 하나의 pointbox를 먹으면 모든 pointbox가 사라지는 문제가 발생했다. 그래서 pointbox의 c# 스크립트에서 onTriggerEnter과 onTriggerExit를 이용해 문제를 해결했다.

 

<실행영상>

 

나는 공부를 하거나 코딩, 인터넷 서칭을 할 때 음악을 거의 항상 듣는다. 노래를 들으며 수학 문제를 풀면 어느 순간 노래의 박자나 리듬을 타며 신나게 문제를 풀고 있는 나를 종종 발견할 수 있었다.

노래의 리듬을 타며 할 일을 하는 것이 나에게는 힐링으로 다가왔고, 내가 좋아하는 노래를 내가 주로 탔던 리듬으로 리듬 게임을 만들고 싶다는 생각으로 이어졌다.

내가 주로 리듬을 타며 들었던 노래들은 팝송이었는데, 유명한 팝송을 이용해 게임을 만들 경우에는 저작권 침해 문제가 발생할 것이라고 생각되어 팝송으로 리듬게임을 만드는 것은 포기했다.

그래서 '베토벤 바이러스'와 같이 음악의 저작권자가 사망한지 70년이 지난 음악들이나 무료로 풀리는 음악들로만 만들고자 한다.

 

많은 리듬게임이 있지만 그중 다수의 게임들은 외우지 않으면 눈감고 클리어를 할 수 없었다. 어릴 때 부터 꽤나 많은 리듬게임들을 해왔지만 나는 보고 누르는 것이 느려 항상 리듬게임을 즐겁게 플레이 하지 못했다. 귀보다는 눈이 더 열일 하는 느낌이 강했다. 그래서 나는 귀가 조금 더 열일 하는 나를 위한 리듬게임을 만들고자 한다. (얼불춤처럼)

 

게임 형태(컨셉)에 관해 일주일동안 고민해 많고 좋은 컨셉들을 생각해냈지만 아직 아는 것이 거의 없는 점을 고려해 실현 가능한 형태로 골랐다.

간단하게 소개하자면 player가 박자에 맞춰 방향 전환을 하며 지그재그로 된 통로에 부딪히지 않는 2D 게임이다.

방향 키는 'A'와 'D' 두 개를 이용할 예정이고, pc 버전으로 먼저 만들고 모바일 버전도 만들어 볼 예정이다.

 

현재까지 작성한 코드는 이러하다.

 

public float Speed;
public int directionIs = 0;

    // Start is called before the first frame update
    void Awake()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        //키입력 시에 방향전환
        if (Input.GetKeyDown(KeyCode.D))
        {
            directionIs = 0;
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            directionIs = 1;
        }
    }

    void FixedUpdate() 
    {
        //Move
        if (directionIs==0)
            transform.position = new Vector2(transform.position.x, transform.position.y + Speed);
        else if (directionIs==1)
            transform.position = new Vector2(transform.position.x + Speed, transform.position.y);
        
    }

 

기본적인 움직임과 키 입력에 따른 방향전환만 구현했다. 

 

 

테스트를 해보았다.

화면 비율은 9:16으로 했다.

영상을 보니 공은 좀 더 앞쪽으로 배치하는 것이 좋을 것 같다. 

'game > unity(C#)' 카테고리의 다른 글

나만의 리듬게임 만들기 - 3  (0) 2022.09.13
나만의 리듬게임 개발일지 - 2  (0) 2022.08.10
2D 기초 - 3 (Jump 구현)  (0) 2022.07.21
2D 기초 - 2 (Player 이동 구현)  (0) 2022.07.14
2D 기초 - 1 (Component, Animation)  (0) 2022.07.12

jump 후 중력을 받아 바닥으로 떨어질 때 뛸 때와 달리 천천히 떨어진다. 이를 조절하기 위해서는 Project Setting에서 Physics 2D에서 Gravity를 바꿔주거나 Rigidbody 2D에서 Gravity Scale을 바꿔주면 된다.

 

제자리에서 jump를 한다면 animation이 breath - jump - breath 순으로 바뀌어야 한다.

이를 위해 오브젝트 검색을 위해 Ray를 쏘는 방식인 RayCast를 이용했다.

DrawRay() - 에디터 상에서만 Ray를 그려주는 함수

Debug이므로 실제 게임창에는 나타나지 않는다.

RayCastHit 변수의 콜라이더로 검색 확인이 가능하다.

LayerMask - 물리 효과를 구분하는 정수값

 

jump animation이 추가되므로 다음과 같이 animator를 설정해주면 된다. <해야할 것>은 똑같다.

 

<코드>

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

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;
    public float jumpPower;
    Rigidbody2D rigid;
    SpriteRenderer spriteRenderer;
    Animator anim;

    // Start is called before the first frame update
    void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }
    // Update is called once per frame

    void Update()
    {   //Jump
        if(Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")){
            rigid.AddForce(Vector2.up*jumpPower, ForceMode2D.Impulse);
            anim.SetBool("isJumping",true);}

        //Stop Speed 
        if(Input.GetButtonUp("Horizontal")){ 
            rigid.velocity = new Vector2(rigid.velocity.normalized.x*0.2f, rigid.velocity.y);
        }
        
        //Direction Sprite
        if(Input.GetButton("Horizontal")){
            spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;
        }

        //Walk <-> Breath
        if(Mathf.Abs(rigid.velocity.x)<0.3f)
            anim.SetBool("isWalking",false);
        else
            anim.SetBool("isWalking",true);
    
    }


    void FixedUpdate()
    {
        //Move By Control
        float h=Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);
        if(rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); // Right Max Speed
        else if(rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed*(-1), rigid.velocity.y); // Left Max Speed

        //Landing Platform
        if(rigid.velocity.y < 0){

            Debug.DrawRay(rigid.position, Vector3.down, new Color(0,1,0));

            RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("platform"));

            if(rayHit.collider != null){
                if(rayHit.distance < 0.5f){
                    anim.SetBool("isJumping", false);
                }
            }
        }
        
    }
}

Player 이동 구현

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

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;
    Rigidbody2D rigid;
    SpriteRenderer spriteRenderer;
    Animator anim;

    // Start is called before the first frame update
    void Awake() {
        rigid = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }
    // Update is called once per frame

    void Update()
    {   
        //Stop Speed 
        if(Input.GetButtonUp("Horizontal")){ 
            rigid.velocity = new Vector2(rigid.velocity.normalized.x*0.2f, rigid.velocity.y);
        }
        
        //Direction Sprite
        if(Input.GetButtonDown("Horizontal")){
            spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;
        }
		
        //Walk <-> Breath
        if(Mathf.Abs(rigid.velocity.x)<0.3f)
            anim.SetBool("isWalking",false);
        else
            anim.SetBool("isWalking",true);
    }
    void FixedUpdate()
    {
        //Move By Control
        float h=Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);
        if(rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); // Right Max Speed
        else if(rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed*(-1), rigid.velocity.y); // Left Max Speed
    }
}

 

단발적인 키 입력은 Updated에서, 계속적인 키 입력은 FixedUpdate에서 해준다.

 

Unity에 있는 기능들을 C# 스크립트로 불러와 적절하게 사용할 수 있다. 이때, 초기화 해주는 것을 잊지 말자.

 

public으로 maxSpeed 변수를 생성해주면 유니티에서 값을 넣어주는 칸이 생성되어 편하다.

 

normalized - 벡터의 크기를 1로 만든 상태이다. (단위벡터)

선형대수학을 배울 때 단위벡터는 방향을 나타내기 위해 많이 쓰였는데 유니티에서도 마찬가지다.

 

python에서의 절댓값 함수 abs와 같은 기능을 하는 것은 Mathf.Abs이다.

Mathf는 수학 관련 함수를 제공하는 class이다.

 


Friction

player가 원활하게 움직일 수 있도록 Physics Material 2D를 생성해 마찰력을 적당히 지정해준다.

참고로 Friction이 마찰력이며, 0으로 지정해줄 경우 마찰력이 없는 빙판과 같다.

오르막길을 잘 오를 수 있도록 하기 위해서 마찰력을 0으로 설정해주었다.

만든 재질을 지형의 Collider 2D - Material에 끌어다 넣어주면 잘 적용된다.

 

 

Rigid Body에서 공기저항 

Linear Drag - 공기저항, 이동 시 속도를 느리게 해준다. 보통 1~2로 설정해준다.

 

 


 

Animator 설정

움직이는 상태, 멈추고 있는 상태 등 상황에 따라 나타낼 player의 animation은 다르므로 코드와 함께 animator를 수정해줘야 한다. breath가 default state인 상태에서 player가 움직일 경우에는 walk 동작을 해야하므로 마우스 우클릭을 통해 breath에서 walk로의 화살표를 만들어 줘야한다. 움직이다가 멈추는 경우도 있으므로 walk에서 breath를 향하는 화살표 또한 만들어 줘야 한다.

 

 

<해야 할 것>

1. Has Exit Time끄기

2. 구간닫기

3. 매개변수 설정

 

 

'game > unity(C#)' 카테고리의 다른 글

나만의 리듬게임 개발일지 - 1  (0) 2022.07.27
2D 기초 - 3 (Jump 구현)  (0) 2022.07.21
2D 기초 - 1 (Component, Animation)  (0) 2022.07.12
Visual Studio Code 자동 완성이 되지 않는 오류  (0) 2022.07.12
0404) C# 기초 - 3  (0) 2022.04.04

Component

collider 2D - 물리 충돌 처리를 해준다. Box collider, circle collider 등 다양한 형태의 collider이 있다.

rigid body 2D - rigid body를 한국어로 하면 강체인데, 강체란 물리학에서 형태가 고정되어 변하지 않는 물체이다. 

이 component를 추가하면 중력이 작용하게 된다.

3D와 똑같은 기능을 하지만 2D에서는 2D가 붙은 component를 추가해야 한다.

 

 

Animation

하나의 캐릭터가 여러가지 동작(숨쉬기, 점프, 걷기 등)을 하게 되면 한 캐릭터에 여러 개의 animation이 들어가게 된다.

이 경우에 바라던대로 animation이 작동하지 않을 수 있는데,

해결 방법은 유니티 상단 바에 있는 window에서 animator 창을 열어 default state를 바꿔주는 것이다.

 

animator 창을 열면 다음과 같이 되어있는 것을 확인할 수 있다.

나는 캐릭터가 걷는 것을 계속하지 않고 숨 쉬기를 계속하기를 원하기 때문에 breath를 default state로 바꿔주었다.

방법은 마우스 우클릭으로 박스를 눌러 Set as Layer Default State로 설정해주면 된다.

 

'game > unity(C#)' 카테고리의 다른 글

2D 기초 - 3 (Jump 구현)  (0) 2022.07.21
2D 기초 - 2 (Player 이동 구현)  (0) 2022.07.14
Visual Studio Code 자동 완성이 되지 않는 오류  (0) 2022.07.12
0404) C# 기초 - 3  (0) 2022.04.04
0401) C# 기초 - 2  (0) 2022.04.01

유니티와 vsc를 연동해 c# script를 작성하고 있는 도중 이전에는 잘 되던 자동 완성이 되지 않는 현상을 발견했다.

자동 완성이 되지 않는 것은 정말 비효율적인 작업을 초래하므로 최대한 빠르게 해결 방법을 찾는 것이 좋다.

 

최근 c# 확장 프로그램이 업데이트 되면서 발생한 현상인 것 같다.

 


 

해결방법

 

C# 확장 프로그램이 아마 1.25.0 버전일텐데 이를 제거 오른쪽에 있는 화살표 버튼을 눌러 1.24.4 버전으로 재설치 해주면 된다.

'game > unity(C#)' 카테고리의 다른 글

2D 기초 - 2 (Player 이동 구현)  (0) 2022.07.14
2D 기초 - 1 (Component, Animation)  (0) 2022.07.12
0404) C# 기초 - 3  (0) 2022.04.04
0401) C# 기초 - 2  (0) 2022.04.01
0331) C# 기초 - 1  (0) 2022.03.31

서론

이 게임은 대학 파이썬 1학년 1학기 전공 수업에서 진행했던 텀프로젝트의 결과물입니다.

파이썬을 배웠기 때문에 파이썬을 이용해 텀프로젝트를 진행해야 했고, 대부분은 파이썬을 이용한 데이터 분석을 주제로 하였지만, 저는 데이터 분석보다는 직접 게임을 만들어 보고 싶었기 때문에 pygame 모듈을 이용해 게임을 만들고자 했습니다. (이는 나중에 후회로 이어지지만...)

처음으로 '무'에서 '유'를 창조한 경험이였기 때문에 무척이나 힘들었지만 그만큼 성취감도 컸던 것 같습니다.

 

본론

이 게임을 프로그램으로 만들기 위해서는 무엇보다도 룰과 게임의 진행방식을 정확하게 파악하는 것이 중요했습니다.

그래서 먼저 룰을 조사했습니다.

 

< 흑과백 2 >

  1. '흑과백 2'는 한정된 포인트를 라운드마다 나누어 대결에 사용하며 더 많은 승점을 획득한 플레이어가 승리하는 게임이다.
  2. '흑과백 2'는 두 플레이어가 각각 99포인트 씩 가지고 시작한다.
  3. 게임은 총 아홉 라운드로 진행되며 플레이어는 라운드마다 99포인트 중 원하는 만큼의 포인트를 사용할 수 있다.
  4. 선 플레이어가 해당 라운드에 사용할 포인트를 결정하면 사용한 포인트가 한 자릿수일 경우 검은색, 두 자릿수일 경우 흰색으로 표시된다. 흑과 백 표시를 단서로 후 플레이어가 포인트를 결정하면 후 플레이어의 포인트 역시 흑과 백으로 표시되며 해당 라운드에서 더 많은 포인트를 사용한 플레이어는 승점 1점을 획득한다.
  5. 사용할 포인트는 소멸되며 남은 포인트는 5단계 표시등으로 공개된다.
  6. 99포인트 중 20포인트 씩 줄어들 때마다 한 단계씩 표시등이 꺼지게 되며 포인트를 입력한 순간 적용된다. 상대방의 남은 포인트가 0점이라도 마지막 표시등은 꺼지지 않는다.
  7. 선 플레이어가 현재 단계보다 낮아지는 포인트를 사용했다면 후 플레이어가 사용할 포인트를 결정하기 전에 표시된다.
  8. 9라운드 종료 시 승점이 더 높은 플레이어가 승리하며 게임 도중 한 플레이어가 승점 5점을 먼저 획득하면 그 즉시 해당 플레이어의 승리로 게임이 종료된다.

 

그러고 나서는 input을 이용해 간단하게 '흑과백2' 게임을 만들어보았습니다. (위에 적힌 룰과 조금 다를 수 있음)

 

player1_point = 99
player2_point = 99
player1_score = 0
player2_score = 0

for i in range(1,10):
    print("Round ",i)
    p1=int(input(prompt="p1)제시할 포인트를 입력해 주세요.")) 
    while player1_point-p1<0:
        p1=int(input(prompt="포인트를 초과하셨습니다. 다시 입력해주세요."))
    player1_point-=p1
    if p1>=0 and p1<10: #한 자리 수 일 경우 "흑"표시
        print("Black")
    else:
        print("White") #두 자리 수 일 경우 "백"표시
    if player1_point<=79 and player1_point>=60:
        print("표시등이 꺼집니다.")
    elif player1_point<=59 and player1_point>=40:
        print("표시등이 꺼집니다.")
    elif player1_point<=39 and player1_point>=20:
        print("표시등이 꺼집니다.")
    elif player1_point<=19 and player1_point>=0:
        print("표시등이 꺼집니다.")

    p2=int(input(prompt="p2)제시할 포인트를 입력해 주세요."))
    while player2_point-p2<0:
        p2=int(input(prompt="포인트를 초과하셨습니다. 다시 입력해주세요."))
    player2_point-=p2
    if p2>=0 and p2<10: 
        print("Black")
    else:
        print("White") 
    if player2_point<=79 and player2_point>=60:
        print("표시등이 꺼집니다.")
    elif player2_point<=59 and player2_point>=40:
        print("표시등이 꺼집니다.")
    elif player2_point<=39 and player2_point>=20:
        print("표시등이 꺼집니다.")
    elif player2_point<=19 and player2_point>=0:
        print("표시등이 꺼집니다.")
    
    #승점 계산
    if p1>p2:
        player1_score+=1
        print("p1 승점 1점 추가")
    elif p1<p2:
        player2_score+=1
        print("p2 승점 1점 추가")
    else:
        print("동점입니다.")

if player1_score>player2_score:
    print("p1 승")
if player1_score<player2_score:
    print("p2 승")

 

그 후, pygame을 어느정도 공부한 후 본격적인 게임 제작에 들어갔습니다.

아래는 pygame을 공부하는 데에 도움이 된 사이트, 책입니다.

 

코드

그리고, 아래는 pygame을 막 배우고, class를 배우기 전에 열심히 쓴 코드입니다.

"""
게임 설명
- 이 게임은 두 명의 플레이어가 대결하는 게임이다.
- 숫자를 입력할 때에는 키보드를 손으로 가려 상대가 보지 못하도록 해야한다.
- '더 지니어스 흑과백2'의 룰, 게임 진행 방식과 동일하다. (관련 영상 : https://www.youtube.com/watch?v=xHcGb_Q4po0)
- 순서는 P1을 시작으로 두 번째 Round 부터는 이전 Round에서 후공이였던 플레이어가 선공이 되어 point를 제시한다.
- point를 입력하고 스페이스바를 누른 뒤, "point is over"라는 문구가 뜨지 않는다면 'z'를 누르도록 한다.
"""

import pygame
import sys

#1. 게임 초기화
pygame.init()

#2. 게임창 옵션 설정
pygame.display.set_caption("Black and White 2")
screen_width = 1200
screen_height = 700
size=(screen_width,screen_height)
screen = pygame.display.set_mode(size)

#3. 게임 내 필요한 설정
clock = pygame.time.Clock()

#color
bg_color = (224,224,224)
black = (0,0,0)
white = (255,255,255)
red = (255, 0, 0)
yellow = (255, 255, 100)
dark_yellow = (120, 120, 0)
color_passive = pygame.Color('gray15')
score_color = (149, 149, 161)
blue = (0, 0, 255)

#font
round_font = pygame.font.SysFont(None, 70)
player_font = pygame.font.SysFont(None, 40)
score_font = pygame.font.SysFont(None, 50)
rect_font = pygame.font.SysFont(None, 40)
light_font = pygame.font.SysFont(None, 32)
warning_font = pygame.font.SysFont(None, 30)

#변수
player1_point = 99
player2_point = 99
player1_score = 0
player2_score = 0

round_number = 1

idx = 0

color_1 = yellow
color_2 = yellow
color_3 = yellow
color_4 = yellow
color_5 = yellow
color_6 = yellow
color_7 = yellow
color_8 = yellow
color_9 = yellow
color_10 = yellow

is_play = 0

#input 
user_input = ''
user_see = ''
active = False

#sound
effect_sound = pygame.mixer.Sound("effectsound.wav")
point_sound = pygame.mixer.Sound("pointsound.wav")
result_sound = pygame.mixer.Sound("resultsound.wav")
yourturn_sound = pygame.mixer.Sound("yourturn.wav")

#4. 메인 (순서 : p1-p2-p2-p1-p1-...)
play = True
while play:

    #FPS 설정
    clock.tick(5)

    
    
    #그리기

    background = pygame.image.load("background.png")
    screen.blit(background, (0,0))

    interfaceBox = pygame.image.load("interfaceBox.png")
    screen.blit(interfaceBox, (400,120))   

    p1_text = player_font.render("Player1", True, red) 
    screen.blit(p1_text, (30,60))
    p1_score_text = score_font.render("{}".format(player1_score), True, red) 
    screen.blit(p1_score_text, (70,500))

    p2_text = player_font.render("Player2", True, blue) 
    screen.blit(p2_text, (screen_width-130,60))
    p2_score_text = score_font.render("{}".format(player2_score), True, blue) 
    screen.blit(p2_score_text, (screen_width-88,500))



    #입력 박스
    input_rect = pygame.Rect(560,400,100,40)
    input_color = pygame.Color('gray15')
    pygame.draw.rect(screen,input_color,input_rect,3)
    text_surface = rect_font.render(user_see,True,(255,255,255))
    screen.blit(text_surface,(input_rect.x+5, input_rect.y+5))



    if idx == 0: #p1 점수입력
        
        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        p1_input_text = rect_font.render("Please enter points", True, red) 
        screen.blit(p1_input_text, (477,160))

        z_text = warning_font.render("press 'space' / no warning --> press 'z'", True, black)
        screen.blit(z_text, (425,450))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if input_rect.collidepoint(event.pos):
                    active = True #active를 True로 바꿔주어 input이 가능하게 함
            if event.type == pygame.KEYDOWN:
                if active == True:
                    if event.key == pygame.K_BACKSPACE: #지우기 기능
                        user_see = user_see[:-1]
                        user_input = user_input[:-1]
                    else:
                        user_see += '*' #블러처리로 상대가 숫자 확인 하지 못하게 함(키보드로 숫자를 입력할 때에는 가리고 해야함)
                        user_input += event.unicode 
                    if event.key == pygame.K_SPACE:
                        p1 = int(user_input[:-1])

                        if player1_point - p1<0:
                            p1_input_text = rect_font.render("point is over", True, black) 
                            screen.blit(p1_input_text, (520,310))
                            user_input=''
                            user_see=''
                            
                            
                        
                        
                    if event.key == pygame.K_z:
                        player1_point-=p1
                        idx = 1
                        is_play = 1

    
        
    elif idx == 1: #p1 결과
        if is_play == 1:
            effect_sound.play()
            is_play = 0

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("next turn => press 'z'", True, black)
        screen.blit(nextTurn_text, (475,180))

        if p1>=0 and p1<10:
            black_text = rect_font.render("Black", True, black)
            screen.blit(black_text, (560,150))
            user_input=''
            user_see=''
        else:
            white_text = rect_font.render("White", True, black)
            screen.blit(white_text, (560,150))
            user_input=''
            user_see=''

        if player1_point<=79 and player1_point>=60:

            color_1=dark_yellow
            
        elif player1_point<=59 and player1_point>=40:
            
            color_1=dark_yellow
            color_2=dark_yellow

        elif player1_point<=39 and player1_point>=20:    
            
            color_1=dark_yellow
            color_2=dark_yellow
            color_3=dark_yellow

        elif player1_point<=19 and player1_point>=0:    
            
            color_1=dark_yellow
            color_2=dark_yellow
            color_3=dark_yellow
            color_4=dark_yellow

        for event in pygame.event.get():            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                        idx = 2
                        is_play = 1




    elif idx == 2: #p2 점수 입력
        if is_play == 1:
            yourturn_sound.play()
            is_play = 0

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        p2_input_text = rect_font.render("Please enter points", True, blue) 
        screen.blit(p2_input_text, (477,160))

        z_text = warning_font.render("press 'space' / no warning --> press 'z'", True, black)
        screen.blit(z_text, (425,450))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if input_rect.collidepoint(event.pos):
                    active = True 
            if event.type == pygame.KEYDOWN:
                if active == True:
                    if event.key == pygame.K_BACKSPACE: 
                        user_input = user_input[:-1]
                        user_see=user_see[:-1]
                    else:
                        user_input += event.unicode 
                        user_see+='*'
                    if event.key == pygame.K_SPACE:
                        p2 = int(user_input[:-1])
                        

                        if player2_point - p2<0:
                            p2_input_text = rect_font.render("point is over", True, black) 
                            screen.blit(p2_input_text, (520,310))
                            user_input=''
                            user_see=''
                        
                        
                    if event.key == pygame.K_z:
                        player2_point-=p2
                        idx = 3
                        is_play = 1
        




    elif idx == 3: #p2 결과
        if is_play == 1:
            effect_sound.play()
            is_play = 0
                    
        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("look result => press 'z'", True, black)
        screen.blit(nextTurn_text, (473,180))


        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    idx = 4     
                    is_play = 1

                    if p1>p2:
                        player1_score+=1    
                    elif p1<p2:
                        player2_score+=1
                                    

        if p2>=0 and p2<10:
            black_text = rect_font.render("Black", True, black)
            screen.blit(black_text, (560,150))
            user_input=''
            user_see=''
        else:
            white_text = rect_font.render("White", True, black)
            screen.blit(white_text, (560,150))
            user_input=''
            user_see=''

        if player2_point<=79 and player2_point>=60:
            color_6=dark_yellow
            
        elif player2_point<=59 and player2_point>=40:
            
            color_6=dark_yellow
            color_7=dark_yellow

        elif player2_point<=39 and player2_point>=20:    
            
            color_6=dark_yellow
            color_7=dark_yellow
            color_8=dark_yellow

        elif player2_point<=19 and player2_point>=0:    
            
            color_6=dark_yellow
            color_7=dark_yellow
            color_8=dark_yellow
            color_9=dark_yellow





    elif idx == 4: #승점 계산
        if is_play == 1:
            point_sound.play()
            is_play = 0

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("next Round => press 'z'", True, black)
        screen.blit(nextTurn_text, (460,350))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    round_number+=1
                    idx = 5     
                    is_play = 1
        if p1>p2:
            player1_win_text = rect_font.render('p1 win', True, red) 
            screen.blit(player1_win_text, (screen_width/2-33,160))
        elif p1<p2:
            player2_win_text = rect_font.render('p2 win', True, blue) 
            screen.blit(player2_win_text, (screen_width/2-33,160))
        else:
            same_text = rect_font.render('same', True, black) 
            screen.blit(same_text, (screen_width/2-33,160))




    elif idx == 7: #p1 점수입력
        if is_play == 1:
            yourturn_sound.play()
            is_play = 0

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        p1_input_text = rect_font.render("Please enter points", True, red) 
        screen.blit(p1_input_text, (477,160))

        z_text = warning_font.render("press 'space' / no warning --> press 'z'", True, black)
        screen.blit(z_text, (425,450))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if input_rect.collidepoint(event.pos):
                    active = True
            if event.type == pygame.KEYDOWN:
                if active == True:
                    if event.key == pygame.K_BACKSPACE: 
                        user_input = user_input[:-1]
                        user_see=user_see[:-1]
                    else:
                        user_input += event.unicode 
                        user_see+='*'
                    if event.key == pygame.K_SPACE:
                        p1 = int(user_input[:-1])

                        if player1_point - p1<0:
                            p1_input_text = rect_font.render("point is over", True, black) 
                            screen.blit(p1_input_text, (520,310))
                            user_input=''
                            user_see=''
                            
                            
                        
                    if event.key == pygame.K_z:
                        player1_point-=p1
                        idx = 8
                        is_play = 1

    
        

    elif idx == 8: #p1 결과
        if is_play == 1:
            effect_sound.play()
            is_play = 0        
        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("look result => press 'z'", True, black)
        screen.blit(nextTurn_text, (473,180))

        if p1>=0 and p1<10:
            black_text = rect_font.render("Black", True, black)
            screen.blit(black_text, (560,150))
            user_input=''
            user_see=''
        else:
            white_text = rect_font.render("White", True, black)
            screen.blit(white_text, (560,150))
            user_input=''
            user_see=''

        if player1_point<=79 and player1_point>=60:

            color_1=dark_yellow
            
        elif player1_point<=59 and player1_point>=40:
            
            color_1=dark_yellow
            color_2=dark_yellow

        elif player1_point<=39 and player1_point>=20:    
            
            color_1=dark_yellow
            color_2=dark_yellow
            color_3=dark_yellow

        elif player1_point<=19 and player1_point>=0:    
            
            color_1=dark_yellow
            color_2=dark_yellow
            color_3=dark_yellow
            color_4=dark_yellow

        for event in pygame.event.get():            
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    idx = 9
                    is_play = 1

                    if p1>p2:
                        player1_score+=1    
                    elif p1<p2:
                        player2_score+=1                    




    elif idx == 5: #p2 점수 입력
        if is_play == 1:
            yourturn_sound.play()
            is_play = 0

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        p2_input_text = rect_font.render("Please enter points", True, blue) 
        screen.blit(p2_input_text, (477,160))

        z_text = warning_font.render("press 'space' / no warning --> press 'z'", True, black)
        screen.blit(z_text, (425,450))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if input_rect.collidepoint(event.pos):
                    active = True 
            if event.type == pygame.KEYDOWN:
                if active == True:
                    if event.key == pygame.K_BACKSPACE: 
                        user_input = user_input[:-1]
                        user_see=user_see[:-1]
                    else:
                        user_input += event.unicode 
                        user_see+='*'
                    if event.key == pygame.K_SPACE:
                        p2 = int(user_input[:-1])
                        
                        if player2_point - p2<0:
                            p2_input_text = rect_font.render("point is over", True, black) 
                            screen.blit(p2_input_text, (520,310))
                            user_input=''
                            user_see=''
                        
                        
                    if event.key == pygame.K_z:
                        player2_point-=p2
                        idx = 6
                        is_play = 1
        



    elif idx == 6: #p2 결과
        if is_play == 1:
            effect_sound.play()
            is_play = 0        

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("next turn => press 'z'", True, black)
        screen.blit(nextTurn_text, (475,180))


        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    idx = 7     
                    is_play = 1

                            
        if p2>=0 and p2<10:
            black_text = rect_font.render("Black", True, black)
            screen.blit(black_text, (560,150))
            user_input=''
            user_see=''
        else:
            white_text = rect_font.render("White", True, black)
            screen.blit(white_text, (560,150))
            user_input=''
            user_see=''

        if player2_point<=79 and player2_point>=60:
            color_6=dark_yellow
            
        elif player2_point<=59 and player2_point>=40:
            
            color_6=dark_yellow
            color_7=dark_yellow

        elif player2_point<=39 and player2_point>=20:    
            
            color_6=dark_yellow
            color_7=dark_yellow
            color_8=dark_yellow

        elif player2_point<=19 and player2_point>=0:    
            
            color_6=dark_yellow
            color_7=dark_yellow
            color_8=dark_yellow
            color_9=dark_yellow




    elif idx == 9: #승점 계산
        if is_play == 1:
            point_sound.play()
            is_play = 0        

        round_text = round_font.render('Round {}'.format(round_number), True, black) 
        screen.blit(round_text, (screen_width/2-83,20))
        nextTurn_text = rect_font.render("next Round => press 'z'", True, black)
        screen.blit(nextTurn_text, (460,350))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z:
                    round_number+=1
                    idx = 0     
                    is_play = 1

        if p1>p2:
            player1_win_text = rect_font.render('p1 win', True, red) 
            screen.blit(player1_win_text, (screen_width/2-33,160))
        elif p1<p2:
            player2_win_text = rect_font.render('p2 win', True, blue)
            screen.blit(player2_win_text, (screen_width/2-33,160))
        else:
            same_text = rect_font.render('same', True, black) 
            screen.blit(same_text, (screen_width/2-33,160))




    elif idx == 10: #종료
        if is_play == 1:
            result_sound.play()
            is_play = 0
        winner_text = rect_font.render('{}'.format(winner), True, black)
        screen.blit(winner_text, (screen_width/2-100,160))
        again_text = rect_font.render("again : 'z'", True, black)
        screen.blit(again_text, (screen_width/2-60,310))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_z: #재시작 reset
                    round_number=1
                    player1_score=0
                    player2_score=0
                    player1_point=99
                    player2_point=99
                    color_1=yellow
                    color_2=yellow
                    color_3=yellow
                    color_4=yellow
                    color_5=yellow
                    color_6=yellow
                    color_7=yellow
                    color_8=yellow
                    color_9=yellow
                    color_10=yellow
                    idx = 0    



    #종료 조건
    if round_number==10 or player1_score==5 or player2_score==5:
        idx=10
        if player1_score == 5:
            winner="Player 1 is winner"
        elif player2_score == 5:
            winner="Player 2 is winner"
        if player1_score > player2_score:
            winner="Player 1 is winner"
        elif player1_score < player2_score:
            winner="Player 2 is winner"
        else:
            winner="     No winner    "
    
    
                    
    
    #잔여 point 표시등
    p1_rect1 = pygame.draw.rect(screen, color_1, (30, 100, 100, 70))  
    p1_rect2 = pygame.draw.rect(screen, color_2, (30, 180, 100, 70))  
    p1_rect3 = pygame.draw.rect(screen, color_3, (30, 260, 100, 70))  
    p1_rect4 = pygame.draw.rect(screen, color_4, (30, 340, 100, 70))  
    p1_rect5 = pygame.draw.rect(screen, color_5, (30, 420, 100, 70))  
    p2_rect1 = pygame.draw.rect(screen, color_6, (screen_width-130, 100, 100, 70))  
    p2_rect2 = pygame.draw.rect(screen, color_7, (screen_width-130, 180, 100, 70))  
    p2_rect3 = pygame.draw.rect(screen, color_8, (screen_width-130, 260, 100, 70))  
    p2_rect4 = pygame.draw.rect(screen, color_9, (screen_width-130, 340, 100, 70))  
    p2_rect5 = pygame.draw.rect(screen, color_10, (screen_width-130, 420, 100, 70))  
    

    p1_rect1_text = light_font.render("80 ~ 99", True, black) 
    screen.blit(p1_rect1_text, (41,125))
    p1_rect2_text = light_font.render("60 ~ 79", True, black) 
    screen.blit(p1_rect2_text, (41,205))
    p1_rect3_text = light_font.render("40 ~ 59", True, black) 
    screen.blit(p1_rect3_text, (41,285))
    p1_rect4_text = light_font.render("20 ~ 39", True, black) 
    screen.blit(p1_rect4_text, (41,365))
    p1_rect5_text = light_font.render("0 ~ 19", True, black) 
    screen.blit(p1_rect5_text, (48,445))
    p2_rect1_text = light_font.render("80 ~ 99", True, black) 
    screen.blit(p2_rect1_text, (screen_width-117,125))
    p2_rect2_text = light_font.render("60 ~ 79", True, black) 
    screen.blit(p2_rect2_text, (screen_width-117,205))
    p2_rect3_text = light_font.render("40 ~ 59", True, black) 
    screen.blit(p2_rect3_text, (screen_width-117,285))
    p2_rect4_text = light_font.render("20 ~ 39", True, black) 
    screen.blit(p2_rect4_text, (screen_width-117,365))
    p2_rect5_text = light_font.render("0 ~ 19", True, black) 
    screen.blit(p2_rect5_text, (screen_width-110,445))
    #업데이트
    pygame.display.flip()



#5. 게임 종료
pygame.quit()

 

나중에 다시 이 코드를 본다면 지저분해 보일 것 같으나 실제로 파이썬을 배우고 사용한지 오래되지 않았고, 처음으로 스스로 제작한 게임이므로 귀엽게 봐주도록 하자.

 

 

최종 플레이 영상

마지막으로 아래는 최종 플레이 영상입니다.

 

 

Review

실제로 제작하며 player의 실수에 따라 발생할 오류들이 몇 개 보였습니다. 나중에 시간이 된다면 이러한 오류들까지 고려한 완벽한 더지니어스 흑과백2 게임으로 업데이트 해서 돌아오도록 하겠습니다.

 

시점 뷰에 대해 공부했다. 명암을 넣을 때는 햇빛이 어디에 있는지 인지해야한다. 사이드 뷰에서 원래의 얼굴 형태를 알맞게 살짝 옆으로 치우치게 하고, 탑 뷰에서는 아래로 치우치게 하면 훨씬 자연스러워 진다.

 

이렇게 말이다.

 

게임에서는 살짝 옆으로 보는 눈을 많이 쓴다고 한다.

캐릭터를 만들 때에는 먼저 기본 틀을 만들어준다. 형태를 정한 뒤에 머리카락을 그리기 시작한다. 귀는 머리카락을 이용해 구렛나루를 만듦으로써 만들 수 있다. 팔은 손 먼저 그리고 연결하여 완성한다. 다리는 발을 크게 그리거나 아예 생략하거나 한다. 손은 허리높이에 위치한다. 반사광으로 인해 생기는 머리카락 하이라이트까지 그려주면 완성이다.

이렇게 왼쪽을 보는 캐릭터를 하나 그려주면 수평 반전으로 오른쪽을 바라보는 캐릭터도 금방 완성할 수 있다.

2D게임을 만들 때 사용할 간단한 캐릭터도 그려보았다. 

만들 게임의 컨셉이 감옥이라 죄수 캐릭터를 그리긴 했는데 컨셉을 유지할 지 말지 현재 고민 중에 있다.

일단 이 간단하고 귀여운 캐릭터를 가지고 간단한 애니메이션들을 만들어 보았다.

 

먼저 눈 깜빡거리기

그 다음은 걷기

마지막으로 가만히 있을 때 숨쉬기

숨 쉬는 애니메이션을 그리는 방법은 머리를 먼저 움직인 다음 몸을 움직이게 하면 된다. (머리 - 몸 순서)

'game > design' 카테고리의 다른 글

도트 공부 시작  (0) 2022.04.05

C# 공부가 지루하던 찰나 곧 2D 게임을 만들건데 그 때 사용할 캐릭터를 도트로 찍을 겸 머리도 식힐겸 스팀에서 판매하는 국룰 도트 프로그램인 aseprite를 구매하고 설치하여 가볍게 기초 강좌를 듣고 그리고 싶은대로 그려봤다.

 

새 파일을 열자말자 아무 지식 없이 처음으로 그린 도트 캐릭터이다. 지금보니 누군가에게 핵펀치로 눈을 맞은 것 같아 보인다.

그렇지만 나는 이 캐릭터에 나름 만족하고 도트강좌의 첫 영상을 시청했다. 그리고 영상에서의 팁을 이용해 처음 만든 캐릭터를 수정해보았다.

 

 

와! 정말 귀엽다. 저 위에 이상한 괴생명체와는 너무나도 비교된다. 이렇게까지 교육을 받고 안받고 차이가 날 줄은 몰랐다. 나는 교육을 안받으면 엉망진창이지만 교육만 받는다면 습득력이 좋은 인재...?

첫 번째 캐릭터와 두 번째 캐릭터의 차이점은 크게 보면 테두리 색, 명암이라는 것을 알 수 있다. 

먼저, 테두리색은 캐릭터 색 계열의 어두운 색(현재 색에서 아래로 쭉 내려가기)으로 처리하는 것이 자연스럽다.

그리고 명암은 현재 캐릭터 색에서 명도와 채도를 둘 다 조금씩 바꾼 색(대각선 오른쪽 방향으로 아주 조금 내려가기)으로 처리하면 된다.

또 다른 팁이 있었는데, 원색(RGBK)을 피하라는 것이다. 원색을 사용하면 캐릭터가 되게 투박해진다고 한다.

영상에서 팔레트를 추천해주었는데 해외 도트 유튜버인 MortMort님의 SoftMilk 32색을 추천해주었다. 파스텔톤으로 이 팔레트를 이용해 배경을 만들면 정말 이쁘다.

 

이후에 주제를 공룡으로 정해 친구와 함께 한번 그려보았다. 먼저 내 도트 그림이다.

 

 

음... 도트로 그리는 솜씨가 연필이나 붓을 가지고 손으로 그리는 솜씨와 많이 유사한 것 같다. 그 다음은 친구의 그림이다.

 

 

잘그렸다. 첫 번째 그림은 얼굴만 그렸지만 되게 리얼한 것 같다.

 

이렇게 도트 입문을 해보았다. C#공부와 도트공부를 이렇게 밸런스 맞추며 한다면 지루함은 줄어들 것이고 흥미는 더 증가 할 것이라고 생각한다.

 


내일은 금요일 다음으로 시간적 여유가 많은 화요일이다. (사실 내일이 아니고 오늘이긴함)

아마 수업을 다 들은 후 수학공부(선형대수, 미분적분학)을 저녁 먹기전까지 하고 저녁 먹고 조금의 휴식을 가진 후 다시 C#과 도트 공부를 하지 않을까 싶다.

'game > design' 카테고리의 다른 글

시점 뷰와 애니메이션  (0) 2022.04.07
int[] exp = {50, 100, 150, 200, 250};
int[] exp2;

 

이렇게 배열을 선언해 줄 수 있다. 배열은 비슷한 성격을 가진 변수들의 집합체이다. 

배열의 값을 참조하고 싶을 때에는 

 

print(exp[0]); //50 (0부터 카운팅하므로)

 

배열의 값을 참조하고 싶을 때에는 위와 같이 하면 된다. (파이썬 공부할 때 배웠던 내용과 동일함)

배열의 원소 개수를 알고 싶을 때 파이썬에서는 len함수를 이용했겠지만 C#에서는 exp.Length를 통해 알 수 있다.

 

int [] array = new int[10];

 

이렇게 하는 것은 배열의 크기(원소의 개수)만 지정해주는 것이다. 

 

방금까지 한 것은 1차원 배열이다.

2차원 배열은 다음과 같이 나타낼 수 있다. (그 이상의 배열들의 형식은 2차원 배열이 생성되는 형식과 유사하다)

 

int[,] array2 = { {1, 2, 3, 4, 5} , {10, 20, 30, 40, 50} };
print(array2[1,3]);

 

코드를 실행해보면 print의 값이 40으로 나오는 것을 알 수 있다. array2[1,3]는 두 배열중 인덱스 1에 해당하는 배열에서의 인덱스 3의 값을 의미한다.

 

그런데 배열은 한번 생성되면 배열의 크기가 고정되어 원소를 늘리거나 줄일 수 없다.

이러한 단점을 극복하는 것이 컬렉션(리스트, 큐, 스텍, 해시테이블, 딕셔너리, 어레이리스트)이다.

 

//ArrayList
ArrayList arrayList = new ArrayList();
arrayList.Add(1); //arrayList에 1이라는 원소를 추가함
arrayList.Add("가나다라");

print(arrayList.Count); //arrayList에서는 length가 없고, Count가 그 기능을 해줌
arrayList[0] = 3; //배열과 사용법은 같음
arrayList.Remove("가나다라"); //직접 지움
arrayList.RemoveAt(0); //인덱스를 통해 지움

 

ArrayList는 누군가가 이미 만들어 놓은 class이다. 점(.)을 찍으면 Add나 Remove같은 메소드를 이용할 수 있다.

 

//List
List<int> list = new List<int>(); //ArrayList와 달리 특정한 자료형만을 원소로 가짐

 

ArrayList는 자료형 상관없이 원소를 가지지만 List는 특정한 자료형을 지정하고, 그 자료형만을 원소로 가진다. 단순히 생각하면 범위가 넓은 ArrayList를 항상 쓰면 더 편하고 좋겠다고 생각되겠지만 범위가 넓은만큼 값이 들어올 때 마다 넓은 Box를 만들고 들어올 때, 나갈 때마다 더 많은 연산이 필요해진다. 즉, 비효율 적인 것이다. 그래서 List보다 더 연산량이 많을 것이고 과부하에 걸리기 쉽다. 

 

//HashTable
HashTable hashTable = new HashTable();
hashTable.Add("만", 10000); //(key, value) 입력
hashTable.Add("백만", 1000000);

print(hashTable["만"]); //인덱스 대신 key로 value를 호출한다

//Dictionary
Dictionary<string, int> dictionary = new Dictionary<string, int>(); //key(string), value(int)

 

HashTable과 Dictionary의 관계는 ArrayList와 List의 관계와 동일하다고 보면 된다.

 

//Queue
Queue<int> queue = new Queue<int>();
queue.Enqueue(5); //원소 5를 넣음
queue.Enqueue(6); //원소 6을 넣음
queue.Dequeue(); //가장 먼저 들어간 원소를 꺼냄
queue.Dequeue(); //두 번째로 들어간 원소를 꺼냄​

 

이 상태에서 queue.Dequeue();를 한번 더 실행하게 된다면 오류가 뜰 것이다. 왜냐면 이미 들어간 총 두개의 원소가 이전에 다 꺼내졌기 때문에 더이상 꺼낼 원소가 없기 때문이다. 그렇기 때문에 queue.Dequeue는 queue.Count!=0일 때에만 실행될 수 있도록 if문과 함께 쓰이는 것이 좋다.

queue는 선입전출이라하고 반대로 후입전출이라고 불리는 stack도 있다.

stack은 queue와 반대로 가장 마지막에 넣었던 원소를 꺼낸다.

 

Stack<int> stack = new Stack<int>();
stack.Push(1); //stack에 1을 넣어줌
stack.Push(2);
stack.Push(3);
stack.pop(); //3을 꺼냄

'game > unity(C#)' 카테고리의 다른 글

2D 기초 - 1 (Component, Animation)  (0) 2022.07.12
Visual Studio Code 자동 완성이 되지 않는 오류  (0) 2022.07.12
0401) C# 기초 - 2  (0) 2022.04.01
0331) C# 기초 - 1  (0) 2022.03.31
메모장  (0) 2022.03.30

+ Recent posts