체스 AI를 완성했다. 제작자가 체스를 못하는 관계로 영상은 같은 AI 로직으로 1:1을 한다. 물론 AI가 완벽하지는 않아서(혹은 AI가 같아서) 후반부로 가면 게임이 끝나지 않는 현상이 발생한다. 어차피 게임은 PVE로 할 방식이라 1~2달 정도 체스를 둔 실력으로는 이기지 못할 것이다.

각설하고, AI를 완성했으므로, 다음주는 여기에 VIVE 및 음성인식(코타나)를 활용하여 마법사 체스를 완성할 것이다. 음성인식이 발음에 영향을 많이 받으므로, VIVE 컨트롤러로 움직이는 방식을 착안해야 할 것 같다.

'프로그래밍 > 게임프로젝트' 카테고리의 다른 글

졸업작품 1주차.  (0) 2018.01.03
posted by 천마서생
:

이번 졸업작품은 해리포터 시리즈에 착안해서 제작한다.


1. Unity를 사용한다.

2. HTC VIVE를 사용한다.

3. Windows 10 이상.

계획은 2월 초까지 완성하는 것을 목표로 한다.


기본적인 컨트롤러 작동은 왼쪽 컨트롤러는 캐릭터 움직임 오른쪽 컨트롤러는 캐릭터의 행동을 통제할 것이다.


전체적인 흐름은 미니게임 3가지로 구성한다. 


1. 마법사 체스.

해리포터와 마법사의 돌에서 나온 마법사 체스를 생각했다.


참고 영상 : https://www.youtube.com/watch?v=imiVDYfoh54


1) Microsoft의 코타나를 사용해서 음성인식을 적용한다.

2) 기본적으로는 PVE를 생각하고 있으나, PVP가 가능하도록 할 것이다.

3) 음성인식의 발음 문제로 인식이 잘 안되는 문제가 생기므로 VIVE Controller도 병행해서 사용하도록 할 것이다.


2. 빗자루 경주

빗자루를 타고 경주하는 게임이다.


참고영상 : https://www.youtube.com/watch?v=yTS1VlvlGRg


1) 오른쪽 컨트롤러 트리거를 사용하면 가속 왼쪽 컨트롤러 트리거를 사용하면 감속한다.

2) 오른쪽 컨트롤러 왼쪽 컨트롤러를 일직선으로 두어야 하며, 오른쪽 컨트롤러의 위치에 따라 방향 전환이 된다. ex) 오른쪽 컨트롤러가 왼쪽 컨트롤러 보다 일정 위치 이상 오른쪽에 있으면 오른쪽으로 왼쪽으로 있으면 왼쪽으로 회전한다.

3) 단순히 컴퓨터와 승부한다. -> 이것도 PVP가 되면 좋으나... 현실을 생각하자.


3. 마법을 사용한 헤르미온느 지키기


아직 구상을 못했다.


일단 1주차로는 체스를 만들었다. 이번주에 Chess AI 알고리즘을 사용하여 PVE가 되도록 할 것이다. 알고리즘은 Alpha Beta 알고리즘을 사용한다.

참고 페이지 : https://chessprogramming.wikispaces.com/Alpha-Beta 

'프로그래밍 > 게임프로젝트' 카테고리의 다른 글

졸업작품 2주차.  (0) 2018.01.12
posted by 천마서생
:
프로그래밍/Unity 2017. 8. 3. 03:13

이 문서는 Unity 2017.1.0f3 ver.에 쓴 문서이다.


VideoPlayer는 Unity 5.6f이상 부터 지원한 기능이다.


* AndroidSupported Media Formats. See additional notes below.
* WindowsH.264 Video Decoder (see Format Constraints)
* iPhone 6-7Compare iPhone Models (see TV and Video)


위에는 각 기기별 지원하는 동영상의 형식이다.

( .dv , .mp4 , .mpg, .mpeg, .m4v , .ogv , .vp8 , .webm )



● Source : Video Clip / URL이 있다.


Video Clip을 이용하면 간단하게 유니티 프로젝트 폴더에 동영상 파일을 넣은 다음 그걸 Drag & Drop 해주면 된다.


URL의 경우에는 http:// 혹은 file:// 을 이용해서 동영상을 찾아서 가져온다. 
(주의사항 : http를 이용할 경우 파일 형식이 .mp4등 실제 동영상 주소를 가져와야한다. 예를들어 유튜브 주소로는 영상을 불러오지 못한다.)


● Play On Awake : Awake때 실행 할지의 여부를 묻는다.


● Wait For First Frame : Play On Awake이 켜져있을때 번째 프레임이 텍스처에 로드 될 때까지 기다릴 지 여부를 결정한다. 끌 경우 초기 몇 프레임이 짤려나가는 불상사가 있을 수 있다.


● Loop : 반복 여부를 묻는다.


● Playback Speed : 영상 재생 속도를 결정한다. 1은 기본 속도이다.


● Render Mode 


Render Mode


Camera Far Plane

영상을 카메라의 Far Plane에서 실행한다.

Camera Near Plane

영상을 카메라의 Near Plane에서 실행한다.

  - Camera

영상을 보여줄 카메라를 넣어준다.

  - Alpha

영상의 알파 값을 적용한다.(알파 값을 낮추면 영상 뒤에 있는 Plane을 볼 수 있다.)

Render Texture

영상을 Texture에서 실행한다.

   - Target Texture

영상을 실행할 Texture을 넣어준다.

Material Override

선택된 GameObject의 Material에서 영상을 실행한다.

  - Renderer

영상을 실행할 GameObject를 넣는다. None일 경우에는 Video Player Component가 적용된 GameObject의 메터리얼에서 실행된다.

  - Material Property비디오를 재생할 메터리얼을 선택한다.
API Only

비디오를 재생할 객체를 스크립트만으로 적용하게 한다.

● Audio Output Mode : 소리를 어떻게 재생 할 것인지 결정한다. 

Audio Output Mode


None

소리를 재생하지 않는다.

Audio Source

오디오 소스를 삽입하여 재생 시킬 수 있다. (Unity 내부의 오디오 처리가 적용 될 수 있다.

Direct

유니티의 내부의 오디오 처리를 거치지 않고 바로 오디오 출력 하드웨어로 전송된다.

● Controlled Tracks : URL에서만 적용 할 수 있다.

비디오의 오디오 트랙 수이다.

소스가 비디오 클립인 경우 비디오 파일을 검사하여 트랙 수를 결정한다.




기본적인 스크립트 사용법


VideoPlayer는 UnityEngine.Video에 있다.


using System.Collections;

using UnityEngine;

using UnityEngine.UI;

using UnityEngine.Video;


public class VideoController : MonoBehaviour

{

    RawImage Image;                                                      

    VideoPlayer vidio;                                                    

    AudioSource audio;

    void Awake()

    {

        Image = GetComponent<RawImage>();

        vidio = gameObject.AddComponent<VideoPlayer>();

        audio = gameObject.AddComponent<AudioSource>();

        vidio.playOnAwake = false;

        audio.playOnAwake = false;

        audio.Pause();

  PlayVideo();

    }

    public void PlayVideo()

    {

        StartCoroutine(playVideo());

    }

    IEnumerator playVideo()

    {

        vidio.source = VideoSource.Url;

        vidio.url = "file://D:/하늘.mp4";


        vidio.audioOutputMode = VideoAudioOutputMode.AudioSource;


        vidio.EnableAudioTrack(0, true);                        

        vidio.SetTargetAudioSource(0, audio);

        vidio.Prepare();

        WaitForSeconds waitTime = new WaitForSeconds(1.0f);

        while (!vidio.isPrepared)

        {

            Debug.Log("동영상 준비중...");

            yield return waitTime;

        }

        Debug.Log("동영상이 준비가 끝났습니다.");

        Image.texture = vidio.texture;

        vidio.Play();

        audio.Play();

        Debug.Log("동영상이 재생됩니다.");

        while (vidio.isPlaying)

        {

            Debug.Log("동영상 재생 시간 : " + Mathf.FloorToInt((float)vidio.time));

            yield return null;

        }

        Debug.Log("영상이 끝났습니다.");

    }

}

'프로그래밍 > Unity' 카테고리의 다른 글

unity JsonUtility 사용법. (저장 / 불러오기)  (0) 2016.07.20
오브젝트 풀  (0) 2016.07.15
LitJSON 파일 있는지 없는지 확인 함수  (0) 2016.07.10
posted by 천마서생
:
프로그래밍/VR 2017. 7. 27. 00:31

기본적으로 Unity5.x, Unity2017에서는 VR을 자동으로 지원해 준다.  Edit -> ProjectSetting -> Player -> Other Setting -> Virtual Reality Supported 라는 체크박스를 체크해주면 끝이다.


문제점은 메인 카메라를 직접 움직일 수가 없다. 따라서 편법 아닌 편법 (유니티에서 권장하는 방법)으로 좌표용으로 쓸 GameObject 한 개를 만들고 그것의 자식으로 Main Camera를 넣어준다. 그다음 GameObject의 좌표를 변경하면 MainCamera도 같이 변경된다. 


VR FPS를 만들 경우 카메라를 캐릭터의 자식으로 붙이고 회전을 마우스로 줘도 된다. ( 헤드 트래킹에 따라서 회전을 시킬 경우 캐릭터가 엎드리거나 누울 수 있으니 헤드 트레킹을 사용할 경우에는 더미 GameObject를 만들어서 캐릭터를 따라다니게 하고 Y축만 따로 가져와서 캐릭터에 적용시키면 된다.

posted by 천마서생
:
프로그래밍/Unity 2016. 7. 20. 14:48

필자가 JSON을 알게 된 것은 nodejs+mongodb를 연환한 서버를 구축할 때 였다.

이 당시에는 JSON의 개념조차 잡혀있지 않았고 JSON이 파일까지 만들어주는 줄 알았다.

(JSON은 파싱 즉 값을 string으로 변환만 해준다.)

결국엔 실패했다.


각설하고 이번 게임 제작할때 SAVE / LOAD를 구현하려고 다시 자료를 찾던중 playerprefabs와 json을 활용한 자료 저장 xml이 있었다. 그중 필자는 저번의 실패를 만회하기 위해 json을 다시 도전했고 자료를 찾던중 유니티 5.3이상부터 지원되는 JsonUtility 기능에 대해서 알게되었다.


https://docs.unity3d.com/ScriptReference/JsonUtility.html    <= 유니티 공식홈페이지

   JsonUtility 레퍼런스


영어로 되어있다.


해석하자면


FromJson

Create an object from its JSON representation.
JSON 형식으로부터 오브젝트를 생성한다.

FromJsonOverwrite

Overwrite data in an object by reading from its JSON representation.
JSON 형식의 데이터를 받아와 오브젝트에 오버라이팅을 한다.

ToJson

Generate a JSON representation of the public fields of an object.
public 형식으로 선언된 오브젝트를 JSON형식으로 저장한다.

여기서 오브젝트란 클래스 / 구조체 / 값 전부 된다.


이제 사용 방법을 보겠다. 

우선 필자는 클래스를 오브젝트로 잡았다.


[SerializeField]

    public class Data

    {

        public string charname;

        public string birthday;

        public int stress;

        public int stamina;

        public int year;

        public int month;

        public int money;

        public bool istired;

        public bool isseek;

    }


아 그리고 모든 오브젝트는 public 과 [SerializeField]로 선언되어야 한다. (이유는 잘 모르겠으나 레퍼런스를 읽어보니 JsonUtility에서 읽고 쓰는 오브젝트들은 SerializeField / public으로 선언되어있어야 사용가능하다고 나옴)


아무튼 필자는 세이브 버튼을 누르면 함수가 호출되도록 설계했다.

 public void Button_save()

    {

        var saveData = new Data();

        saveData.charname = m_charactorname;

        saveData.birthday = m_birthday;

        saveData.stress = m_stress;

        saveData.stamina = m_stamina;

        saveData.year = m_year;

        saveData.month = m_month;

        saveData.money = m_money;

        saveData.istired = m_istired;

        saveData.isseek = m_isseek;

        string save = JsonUtility.ToJson(saveData, prettyPrint: true);

  Debug.Log(save);


        pathForDocumentsFile("save.txt");       //파일 입출력 용 함수

        writeStringToFile(save1, "save.txt");      //파일 입출력 용 함수

    }

이런 식으로 만들었다.

우선적으로 클래스를 saveData로 동적생성 해주고 각각의 값을 저장했다.

그 후 string save = JsonUtility.ToJson(saveData, prettyPrint: true); 이렇게 적었다.

JsonUtility 레퍼런스에서 JsonUtility.ToJson의 반환 값이 string이므로 string 변수를 생성 후 그 곳에 저장한다. 그리고 Debug.Log()를 해보면 Json형식으로 바뀐 값을 볼 수있다.

여기서 그만두면 string에만 저장되있고 파일로는 저장이 안된다. 따라서 이제 string을 파일입출력을 통해 txt파일로 저장하면된다!


이제 저장을 했으니 불러오기를 할 차례이다.

이거 또한 Load 버튼을 누르면 함수가 호출되도록 설계했다.

 public void Button_load()

    {

        pathForDocumentsFile("save.txt");                        //파일 입출력 용 함수

        string load = readStringFromFile("save.txt");           //파일 입출력 용 함수

        var loadData = JsonUtility.FromJson<Data>(load);

        m_charactorname = loadData.charname;

        m_birthday = loadData.birthday;

        m_stress = loadData.stress;

        m_stamina = loadData.stamina;

        m_year = loadData.year;

        m_month = loadData.month;

        m_money = loadData.money;

        m_istired = loadData.istired;

        m_isseek = loadData.isseek;

    }


세이브와는 정 반대이다.   var loadData = JsonUtility.FromJson<Data>(load); 

Data클래스의 형식으로 Json으로 되어있는 파일을 바꿔준다.


JsonUtility.FromJson<T>(string );

레퍼런스에서는 이러한 구조로 되어있다. 따라서 T 부분에 자기가 원하는 저장시 데이터 상태를 넣어주고 string 부분에 Json형식이 저장되 있는 것을 넣어주면된다.

'프로그래밍 > Unity' 카테고리의 다른 글

유니티 VideoPlayer.  (0) 2017.08.03
오브젝트 풀  (0) 2016.07.15
LitJSON 파일 있는지 없는지 확인 함수  (0) 2016.07.10
posted by 천마서생
:
프로그래밍/Unity 2016. 7. 15. 19:43

유니티는 플랫폼이 다양하다.
그 중 모바일 플랫폼도 쓰는데, 모바일 플랫폼에서 게임오브젝트 또는 프리팹을 동적으로 생성하는 작업(Instantiate 함수)은 플랫폼 특성상 과부하가 걸릴 수 밖에 없다. 따라서 주기적 / 반복적으로 생성하는 객체는 씬이 처음 로드할 때 모두 생성한 후 사용하는 방식이 속도면에서 유리하다(렉을 방지한다). 이처럼 사용할 객체를 미리 만들어 놓은 후 필요할 때 가져다 사용하는 방식을 오브젝트 풀링이라고 한다.


ex) GameManager.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic; // List 자료형을 사용하기 위해 추가해야 하는 네임스페이스
public class GameManager : MonoBehaviour
{
public Transform[] points;
public GameObject monsterPrefab;
public List<GameObject> monsterPool = new List<GameObject>(); //몬스터를 미리 생성해 저장할 리스트 자료형
public float createTime = 2.0f;
public int MaxMonster = 10;
public bool isGameOver = false;
public static GameManager instace - null; // 싱글턴 패턴을 위한 인스턴스 변수 선언
void Awake()
{
//GameManager 클래스를 인스턴스에 대입
instance = this;
}
void Start()
{
points = GameObject.Find("SpawnPoint").GetComponentsInChildren<Tranform>();
for(int i=0;i<maxMonster;i++)
{
GameObject monster = (GameObject)Instantiate(monsterPrefab);
monster.name = "Monster_" + i.ToString();
monster.SetActive(false);
monsterPool.Add(monster);
}
if(points.Length>0)
{
StartCoroutine(this.CreateMonster());
}
}
IEnumerator CreateMonster()
{
while(!isGameOver)
{
int monsterCount = (int) GameObject.FindGameObjectsWithTag("Monster").Length;
if(monsterCount < maxMonster)
{
yield return new WaitForSeconds(createTime);
int idx = Random.Range(1,points.Length);
Instantiate(monsterPrefab,points[idx].position,points[idx].rotation);
}
else
{
yield return null;
}
}
}
}


posted by 천마서생
:
프로그래밍/Unity 2016. 7. 10. 20:20

static public bool JsonDataContainsKey(JsonData data,string key) { bool result = false; if(data == null) return result; if(!data.IsObject) { return result; } IDictionary tdictionary = data as IDictionary; if(tdictionary == null) return result; if(tdictionary.Contains(key)) { result = true; } return result; }




LitJSON의 JsonData 에 해당 key가 존재하는지 체크하는 함수



'프로그래밍 > Unity' 카테고리의 다른 글

유니티 VideoPlayer.  (0) 2017.08.03
unity JsonUtility 사용법. (저장 / 불러오기)  (0) 2016.07.20
오브젝트 풀  (0) 2016.07.15
posted by 천마서생
:
프로그래밍/C++언어 2016. 6. 7. 04:49

맨 처음 문제가

1. 포인터

2. 동적할당

3. 파일입출력

4. 텍스트 게임 금지(글로만 된 스토리 게임)


필자는 곰곰히 생각하고 하루만에 구현 할 수 있는 쉽고 재밌는 게임인 오목을 만들기로 했다. 


또한, 규칙은 복잡하니 단 하나 5개이상으로 연결만 되면 승리 라는 소박한 규칙만 만들었다.


맨 처음으로 Define.h / Define.cpp 를 만들었다. 필자의 프로그래밍 습관이기도 한데 #include XXX 이렇게 계속 선언할 것을 그냥 헤더파일 하나로 모았다.

// Define.h

#pragma once

#include<iostream>

#include<Windows.h>

#include<conio.h>

using namespace std;

void gotoxy(int x, int y);  // 이동을 지시해 줄 함수

enum { LEFT = 75, RIGHT = 77, UP = 72, DOWN = 80, SPACE = 32 };


// Define.cpp

#include"Define.h"

void gotoxy(int x, int y)

{

COORD Pos = { x,y };

SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos);

}


그 다음 게임을 만들기 위해서 이동을 해야되지 않겠는가? 필자는 CPoint.h / CPoint.cpp로 그리는 함수, 움직이게 키를 받는 함수, 승리 조건 체크 함수를 만들었다.


// CPoint.h

#pragma once

#include"map.h"

class CPoint : public MAP

{

int x; // x좌표

int y; // y좌표

public:

CPoint(int x, int y); // 생성자

void screen(); // 그리는 함수

void input(int &p); //키를 입력 받는 함수

bool check(int &p); // 승리 조건 체크 함수

};


// CPoint.cpp

#include"CPoint.h"

CPoint::CPoint(int x, int y)

{

this->x = x;

this->y = y;

}

void CPoint::screen()

{

system("cls");

for (int j = 0; j < Get_height(); j++)

{

for (int i = 0; i < Get_width(); i++)

{

if (xy[j][i] == 1)

cout << "★";

else if (xy[j][i] == 2)

cout << "☆";

else if (xy[j][i] == 0)

cout << "╋";

}

cout << "\n";

}

}

void CPoint::input(int &p)

{

int c;

bool turn = true;

while (turn)

{

c = getch();

switch (c)

{

case RIGHT:

{

++x;

if (x >= Get_width() - 1)

--x;

gotoxy(x * 2, y);

break;

}

case LEFT:

{

--x;

if (x <= 0)

++x;

gotoxy(x * 2, y);

break;

}

case UP:

{

--y;

if (y <= 0)

++y;

gotoxy(x * 2, y);

break;

}

case DOWN:

{

++y;

if (y >= Get_height() - 1)

--y;

gotoxy(x * 2, y);

break;

}

case SPACE:

if (xy[y][x] != 0)

break;

if (p == 2)

{

xy[y][x] = 2;

}

else

{

xy[y][x] = 1;

}

turn = false;

break;

}

}

}

bool CPoint::check(int &p)

{

int j, sum = 0;

for (j = -5; j <= 5; j++) // 좌우 체크

{

if (x + j <= 0 || x + j >= Get_width())

continue;

if (p == xy[y][x + j])

{

sum++;

if (sum == 5)

return false;

}

else

sum = 0;

}

for (j = -5; j <= 5; j++) // 상하 체크

{

if (y + j <= 0 || y + j >= Get_height())

continue;

if (p == xy[y + j][x])

{

sum++;

if (sum == 5)

return false;

}

else

sum = 0;

}

for (j = -5; j <= 5; j++) // 오른쪽 위 왼쪽 아래 체크

{

if (y + j <= 0 || y + j >= Get_height())

continue;

if (x + j <= 0 || x + j >= Get_width())

continue;

if (p == xy[y + j][x + j])

{

sum++;

if (sum == 5)

return false;

}

else

sum = 0;

}

for (j = -5; j <= 5; j++) // 왼쪽 위 오른쪽 아래 체크

{

if (y + j <= 0 || y + j >= Get_height())

continue;

if (x + j <= 0 || x + j >= Get_width())

continue;

if (p == xy[y + j][x - j])

{

sum++;

if (sum == 5)

return false;

}

else

sum = 0;

}

return true;

}


그 다음 맵을 만들려고 했다. 그런데 아까 문제중 파일 입출력을 사용하라고 했는데 오목에서 파일 입출력을 받을 것이라고는 맵 / 랭킹정도 밖에 없다고 생각했다. 하지만 필자는 하루동안 (4시간) 만들어야하는 사명을 가지고 있으므로 맵만 파일입출력을 받기로 했다. 맵 받는게 무진장 쉽다고 생각한 것은 내 착오였다. 

일단 텍스트로 맵 정보를 받는데, 그 크기를 자유자제로 조정할 수 있어야 하므로 맵을 읽어 드리는 2차원 배열또한 크기가 자유자제로 바뀌어야한다. 하지만 여러분들은 (필자포함) 배열의 인덱스는 변경될 수 없다고 배웠을 것이다. 실제로 배열 인덱스에 변수를 넣으면 "변수 넣지마 꺼져" 라는 컴파일 에러를 발견하게 될 것이다 (VS2015기준 / C99였나부터 잘 모르겠지만 가변변수가 배열 인덱스에 허용된다고 한다.) 


따라서 필자는 동적할당으로 배열을 만들겠다는 망상을 가지고 map.h / map.cpp를 만들었다.


//map.h

#pragma once

#include"Define.h"

class MAP

{

private:

int width; // 넓이

int height; // 높이

public:

int **xy=NULL; // 동적할당을 위한 더블포인터 xy

void Init(); // 초기화

void Release(); // 메모리 해제

int Get_width(); // 넓이 가져오기

int Get_height(); // 높이 가져오기

};


//map.cpp

#include"map.h"

void MAP::Init()

{

FILE *fp = fopen("mapsize.txt", "r");

int ret = 0;

while (ret != EOF)

ret = fscanf(fp, "%d %d", &width, &height);

xy = new int*[height];

for (int i = 0; i < height; i++)

{

xy[i] = new int[width];

memset(xy[i], 0, sizeof(int)*width);

}

}

void MAP::Release()

{

for (int i = 0; i < height; i++)

delete xy[i];

}

int MAP::Get_height() { return height; }

int MAP::Get_width() { return width; }



마지막으로 대망의 main.cpp를 만들 차례이다!


//main.cpp

#include"Define.h"

#include"CPoint.h"

int main()

{

CPoint cpt(1, 1);

cpt.Init();

int user = 1;

bool check = true;

char name[2][256];

for (int i = 0; i < 2; i++)

{

cout << i + 1 << "번째 님의 이름을 입력:\n";

cin >> name[i];

}

for (int j = 0; j < cpt.Get_height(); j++)

for (int i = 0; i < cpt.Get_width(); i++)

cpt.xy[j][i] = 0;

cpt.screen();

while (check)

{

gotoxy(0, 20);

cout << name[user - 1] << "님 차례";

cpt.input(user);

cpt.screen();

check = cpt.check(user);

if (user == 1)

user = 2;

else

user = 1;

}

gotoxy(0, 20);

cout << name[user - 2] << "님의 승리" << endl;

cpt.Release();

}


이것으로 오목을 4시간(실제 작업시간은 7시간) 만에 만들고 잤다. 사실 시간이 가장 걸렸던건 2차원 배열을 동적으로 받는것에서 3시간을 날렸다. 후... 다시는 하기 싫은 노가다였다.

posted by 천마서생
:
프로그래밍/C언어 2016. 6. 7. 04:31

이번에 해볼것은 저번에 했던 우주선에서 배경을 텍스트 파일로 불러오는 것이다.

한마디로 요약하자면 배열에 들어 갈 값을 텍스트파일로 받아오는것이다.


엄청나게 간단하다.


int map[WIDTH][HEIGHT]; 일단 이렇게 2차원의 map이라는 배열을 선언해 준 후 



이렇게 메모장에 배경과 맵을 만들어 준다.


그리고 

FILE *fp = fopen("map.txt", "r");

for (int i = 0; i<WIDTH; i++)

for (int j = 0; j<HEIGHT; j++)

fscanf(fp, "%d", &map[i][j]);

이렇게 해주면 끝이다. fscanf 는 공백까지 읽어드리므로 그냥 1하고 스페이스바 한번 누르고 1 이렇게 쓰면 된다.

posted by 천마서생
:
프로그래밍/C언어 2016. 6. 7. 04:24

조건

1. 배열의 크기 상관없음.

2. 우주선 모양 상관없음.

3. 구름(▩)의 모양, 속도 상관없음. 갯수는 적어도 세 개 이상.

4. 테두리 지워지지 말것.

5. 우주선이 구름에 가려지면 안 됨. 그 반대로 구현해야함.


-----------------------------------------------------------------------------------------------------------



솔직히 우주선이라 보기에는 너무 대충만들었다.

그래도 일단 시작해보자. 

필자는 이번 문제를 보면서 생각한 것이 우주선은 어쩌피 안 움직이고 뒤에 배경 즉, 구름만 움직여야 한다는 조건을 듣고 생각을 해봤다. "차라리 우주선을 배경으로 씌워버리면 어떨까? 어쩌피 우주선은 고정이니깐" 이런 생각을 가지고 저번 포스팅때 만든 맵을 그대로 가져왔다.

우주선이 해결되니 남은 것은 구름이다. 우주선은 기본적으로 움직여야 하니 구름은 스스로 내려가도록 설정해야한다. 따라서 필자는 AutoMove() 라는 함수를 새로 만들었다.

AutoMove()

{

m_y++;

}

맨 처음에는 이렇게 짰었다. 하지만 구름은 미친듯한 속도로 내려가는 걸 보고 절망에 빠졌고 속도를 늦출 방법을 생각해야했다. 그래서 검색을 하던중 time.h에 시간에 관련된 함수가 있는 것을 보고 delay(clock_h n) 이라는 함수라는 것을 새로 만들었다. delay함수는 이 함수를 컴파일러가 읽으면 n값 만큼 시간을 지연시켜주는 함수이다.


void delay(clock_t n)

{

clock_t start = clock();

while (clock() - start < n);

}

AutoMove()

{

delay(100);

m_y++;

}

이렇게 설정을 해주고 나니 이제 남은 것은 구름과 테두리를 충돌 체크 해줘야한다.

 

충돌 체크는 의외로 쉬웠다.오른쪽으로 움직일 시 구름의 좌표 + 구름의 길이 +1의 값이 BLOCK 즉 테두리 일 경우 입력을 못 받게 했으면 왼쪽으로 움직일 시 구름의 좌표 -1의 값을 체크했다. 이렇게 완성을 했다.


이번에도 소스코드는 C++이다. 하지만 전편에서도 말했지만 딱히 상관은 없다. C++의 꽃인 상속(다형성)은 1도 안썼다. 





posted by 천마서생
: