반응형

오늘은 3DS MAX에서 모델링할 때 뒷배경을 바탕으로 모델링을 하게 됩니다

그래서 뒷배경을 세팅할 때의 팁을 설명하고자 합니다 

3DS max를 열고 Geometry 아이콘을 선택하여 Plane을 생성합니다 

그리고 Parameters를 원래 뒷 배경으로 사용할 가로 세로 크기를 넣습니다 

포토샵에서 imgw size로 들어가면 쉽게 이미지 크기를 알아볼 수 있습니다 

 

 

plane 위치를 x:0 , y:90, z:0으로 맞춥니다 

 

 

plane을 선택하고 Shift 키를 누르고 copy 합니다. 그리고 z 축 방향으로 -90도를 틀어서  위치를 x -90, y 0, z 0로 놓습니다

 

 

 

Plane을 선택하고 Material Editor 아이콘을 클릭하여 들어갑니다

 

Standard  메터리얼을 선택하여 드래그해서 view 화면에 나타나게 합니다 

 

Bitmap을 선택하여 view화면에 나타나게 하고 Standard Material에 Diffuse Color에 연결합니다 

 

 

Bitmap을 선택하고 아래 그림과 같이 Bitmap의 None을 선택하여 백그라운드로 사용할 그림을 선택합니다

 

Plane을 선택하고 Assign Material to Seletion을 누릅니다 그리고 Show Shaded Material in Viewport를 선택합니다 그러면 화면에서 백그라운드 그림이 나타나게 될 것입니다 

 

copy 하였던 Plane2 도 마찬가지로 합니다  

 

 

Plane을 선택하여 마우스 오른쪽 버튼을 누르고  Object Properties를 선택합니다  

 

General에서 Freeze를 선택하고 BackfaceCull과 Edges Only를 선택되게 합니다 그리고 Show Frozen in Gray를 선택 해제합니다 

 

Plane2도 Plane과 같이 세팅합니다 

 

 

아래 동영상과 같이 나오면 세팅이 완료되었습니다 

 

반응형
반응형

포토샵 작업 중 그림파일을 layer 단독으로 사용하고 싶을 때가 있습니다

포토샵에서 쌓여있는 레이어를 단독으로 저장하고 싶을 때는 작업중인 해당 레이어를 선택합니다 

 

레이어를 선택한후 마우스 오른쪽 버튼을 누르면 아래 그림과 같이 Export As를 선택합니다

 

그렇게 하면 아래 그림과 같이 내보내기 형식의 창의 뜨게 됩니다 여기서 파일 설정을 하고 모두 내보내기를 클릭하면 됩니다 

 

 

아래 화면과 같이 파일이 저장되어진 모습입니다 

간단하게 유용하게 써먹을수 있는 포토샵(Photoshop)  레이어 기능이었습니다 

 

반응형
반응형

f-5 전투기 3DS max로 모델링을 하였습니다 

우리나라에선 제공호로 잘알려진 전투기입니다 

 

실제 제원은 

F-5E 전투기 

전장 14.45m

전폭 8.13m

전고 4.08m

자체중량 4.4t

최대 이륙 중량 11.2t

최고 속도 마하 1.6

실용 상승한도 15,800m

전투행동반경 1,405km

항속거리 3,700km

엔진 j85-GE-21B 터보제트(3,500/5,000ibs) 2기

항전장비 AN/APQ-153 레이더(최대탐지거리 약 42km) ASG-29 광학 조준기

무장 M39 20mm 기관포 2정 AIM-9 단거리 공대공 미사일, 자유낙하 폭탄, 지상공격용 로켓

생산대수 1,399대  

 

전투기의 대각선 옆

 

전투기의 윗면

 

 

전투기의 대각선 옆 2

 

맥스 작업 폴리곤 작업 모습

 

전투기 옆

 

전투기 앞

 

 

1950년대 미구 노스롭 그루먼의 전신인 노스롭 사가 개발한 경량급 전투기입니다 

별명은 f-5E/F 타이거Ⅱ

f-5 전투기는 미국이 2급 동맹국에게 싼 가격으로 뿌릴 목적으로 선택 한기종입니다 2급 동맹국 중에는 대한민국도 포함되어 있었고 우리나라도 도입하게 되었습니다

미군에서는 지금도 가상적기로 f-5e 가 사용되어 훈련하고 있습니다  

 

반응형
반응형

1인칭 공포 사격게임 심장이 조여드는 공포의 사격이 시작됩니다 

끊임없이 몰려오는 요괴들을 최신 총으로 무장하여 무찔러야 합니다 

심장이 조여오는 공포감의 끝판왕!!

 

구글플레이 

https://play.google.com/store/apps/details?id=com.hoseakCompany.GunShootingGame

 

이상한 요괴들이 쉴틈을 안 주고 계속 몰려 옴니다 

 

황금을 얻기 위해 음침하고 요괴들이 많이 출 물 하는 숲에서 요괴들을 사냥합니다
황금을 얻기 위해 많은 요괴를 잡아야 합니다
스테이지마다 60초 동안 요괴 사냥을 합니다
요괴를 많이 잡을수록 황금을 많이 얻습니다
요괴들을 사냥하지 못하고 요괴의 공격을 받으면 에너지가 줄어듭니다
스테이지가 올라 갈수록 요괴들이 많이 나옵니다
황금을 많이 얻으면 더욱 업그레이드된 사냥총을 구입할 수 있습니다

Facebook 페이지 ( https://www.facebook.com/yeamuzin )에서 좋아요를 눌러 주세요

twitter ( https://twitter.com/yeamuzin )

티스토리 ( https://magatron.tistory.com ) 많은 정보를 공유합니다

 

게임 다운로드

반응형
반응형

탱크 게임 만들기 영상

 

 

Textrue 파일

tankgameTexure.unitypackage
0.06MB

유니티 프로젝트 파일을 만들고  Scenes 파일을 열여 씬 이름을 TankGame으로 바꿉니다 

 

 

 

Texture파일을 만들고 Textrure 파일을 임포트 합니다  파일을 유니티로 끌어오거나 아니면 Texture 폴더를 선택하고 마우스 오른쪽 버튼을 누르고 import package -> Custorm Package를 선택하여 다운로드한 Textrure 파일을 선택하면 됩니다 

 

Game 화면을 아래 그림과 같이 Type ->Filxed Resolution 을 1024 476으로 맞춥니다

 

 

 

하이 라키 뷰에서 BackGround 오브젝트를 만들고 Add Compnent를 선택하여 Sprite Renderer를 선택하고 그림 background를 넣고 SortingLayer를 Background를 생성하여 Order in Layer를 0으로 맞춥니다 

 

Game 뷰에서 아래 그림과 같은 화면이 됩니다 

 

 

하이 라키 뷰에서 Player 오브젝트를 만들고 Tag와 Layer를 Player 로 합니다 

 

 

 

Player 오브젝트 자식으로 Tank_body 오브젝트를 만들고 그림 tank_body를 넣고 Sorting Layer를 Add Sorting Layer 를 누르고   Player 생성하고  Order in Layer 0으로 맞춥니다 

Sorting Layer 순서입니다 

 

Player 자식으로 Tank_top 오브젝트를 만들고 Sprite Renderer를 생성하여 tank_cannon을 넣고 SortingLayer -> Player

Order in Layer ->1로 합니다 

 

 

 

 

하이 라키 뷰에서 Player 오브젝트를 선택하고 Rigidbody2 D를 붙이고 Gravity Scale ->0으로 하고 Box Collider2D를 붙입니다

 

Scripts 폴더를 만들고 Player 스크립트를 생성하여 작성합니다 

 

 

 

Player Scripts작성

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

public class Player : MonoBehaviour
{
    public float speed; //탱크 스피드

    private Rigidbody2D rb;

    private Vector2 moveAmount;//탱크 움직임 방향

    public float health;//탱크 체력

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //탱크의 방향을 키보드 ad(좌우) ws(위,아래) 설정
        Vector2 moveInput = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        //탱크방향에 속도를곱한다
        moveAmount = moveInput.normalized * speed;
    }

    //탱크가 움직일수 있도록 프레임을 곱한다
    private void FixedUpdate()
    {
        rb.MovePosition(rb.position + moveAmount * Time.fixedDeltaTime);
    }

    //적의 공격을 받을 때 체력을 감소시킨다
    public void TakeDamage(int damageAmount)
    {
        health -= damageAmount;
        if (health <= 0)
        {
            Destroy(gameObject);
        }
    }
}

 

 

작성한 Player스크립트를 Player 오브젝트에 붙입니다 

 

 

TankTopCtrl 스크립트를 생성하고 작성합니다 

 

 

 

TankTopCtrl스크립트 작성

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

public class TankTopCtrl : MonoBehaviour
{
    void Update()
    {
        Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
        transform.rotation = rotation;
    }
}

 

Player 자식으로 있는 Tank_top 오브젝트를 선택하고 TankTopCtrl 스크립트를 붙힘니다 

 

 

 

 

아래 동영상과 같이 탱크가 움직이고 마우스 움직임에 따라 포탑이 움직이면 됩니다 

 

반응형
반응형

포토샵 이미지 작업을 하면 레이어로 나눈 이미지를 순번대로 따로따로 저장하고 싶을 때가 있습니다 

그래서 레이어 이미지를 분리하여 따로 저장하는 방법을 알아 보려 합니다 

 

아래 그림과 같이 캐릭터가 있는데 팔 다리 몸통 머리 나머지 관절 부분이 레이어로 나누어 작업되어졌습니다 

 

먼저 Export -> Layers to files 를 선택하여 들어갑니다 

 

그러면 이미지 파일을 저장할 Brouwe를 선택하고 변환할 이미지 파일 타입을 선택한 다음 Run을 누릅니다 

 

 

그러면 아래 그림과 같이 레이어 이미지가 각각 이미지 파일로 나누어져 저장하게 됩니다 

게임 작업할때 캐릭터를 게임 엔진으로 옮길 때 유용하게 사용할 수 있습니다 

반응형
반응형

하이 라키 뷰에서 마우스 오른쪽 버튼을 누르고 UI -> Text를 만듭니다 

3개를 만들어서 하나는 이름을 LiveText 하고 화면 오른쪽 위에 놓고 나머지 두 개는 GameOver, Success 라 하고 중앙에 놓습니다 

 

 

 

 

 

 

 

 

Scripts 폴더에 GameManager 를 만들고 스크립트를 작성합니다 

 

GameManager 스크립트 작성

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

public class GameManager : MonoBehaviour
{
    //남은 생명력
    public int lives = 3;

    //벽돌갯수
    public int bricks = 32;

    //게임 재시작 시간
    public float resetDelay;


    public Text txtLives = null;
    public GameObject gameOver = null;
    public GameObject success = null;
    public GameObject bricksPrefab;
    public GameObject paddle = null;
    public GameObject DeathParticles = null;
    public static GameManager Instance = null;

    private GameObject clonePaddle = null;


    void Start()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else if (Instance != this)
        {
            Destroy(gameObject);
        }

        SetUp();
    }

    //게임 시작시 패들과 벽돌을 불러온다
    public void SetUp()
    {
        if (paddle != null)
        {
            clonePaddle = Instantiate(paddle, paddle.transform.position, Quaternion.identity) as GameObject;
        }

        if (bricksPrefab != null)
        {
            Instantiate(bricksPrefab, bricksPrefab.transform.position, Quaternion.identity);
        }
    }


    //게임 재시작 설정
    void CheckGamOver()
    {
        //벽돌을 깻을 때
        if (bricks < 1)
        {
            if (success != null)
            {
                success.SetActive(true);
                //시간을 2.5배 빠르게
                Time.timeScale = 2.5f;
                Invoke("Reset", resetDelay);
            }
        }

        //생명력을 소진했을때
        if (lives < 1)
        {
            if (gameOver != null)
            {
                gameOver.SetActive(true);
                //시간을 0.25배 느리게 
                Time.timeScale = 0.25f;
                Invoke("Reset", resetDelay);
            }
        }

    }

    private void Reset()
    {
        //평균타임을 설정합니다
        Time.timeScale = 1f;

        Application.LoadLevel(Application.loadedLevel);
    }

    //생명력을 잃게되면 발생
    public void LoseLife()
    {
        lives--;

        if (txtLives != null)
        {
            txtLives.text = "남은 생명 : " + lives;
        }

        //파티클 발생
        if (DeathParticles != null)
        {
            Instantiate(DeathParticles, clonePaddle.transform.position, Quaternion.identity);
        }

        //패들 없애기
        Destroy(clonePaddle.gameObject);

        //딜레이 시간 만큼 지나면 패들 생산
        Invoke("SetupPaddle", resetDelay);
        CheckGamOver();
    }

    //패들 생산 
    private void SetupPaddle()
    {
        clonePaddle = Instantiate(paddle, transform.position, Quaternion.identity) as GameObject;
    }

    public void DestroyBrick()
    {
        bricks--;
        CheckGamOver();
    }

}

 

 

 

 

 

 

 

작성한 스크립트를 하이라키뷰에 GameManager 오브젝트를 만들 과 스크립트를 붙입니다

 

하이 라키 뷰에 오른쪽 마우스 버튼을 클릭하여 Effects ->Particle System을 생성시켜 이름을 hitParticle이라고 합니다 

그리고 아래 그림과 같이 속성을 바꿉니다

 

 

 

 

 

 

 

 만든  이팩트의 모습

 

이팩트를 프리 팹으로 만듭니다

 

Scripts 폴더에  BlocklCtrl 스크립트를 만듭니다

 

 

 

 

 

 

 

 

BlockCtrl 스크립트 작성

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

public class BlockCtrl : MonoBehaviour
{

    public GameObject brickParticle ;

    //공과의 충동할때
    private void OnCollisionEnter(Collision other)
    {
        if (brickParticle != null)
        {
            //이팩트를  발생시킨다
            Instantiate(brickParticle, transform.position, Quaternion.identity);

            GameManager.Instance.DestroyBrick();
            Destroy(gameObject);
        }

    }
}

 

block 오브젝트 을 열고 각각 32개 블록 Cube에 스크립트를 붙이고 BlockCtrl 스크립트의 Brick Particle에 hitParticle을 붙입니다

 

 

 

 

 

 

 

하이 라키 뷰에 있는 프리 팹들을 Overrides 합니다 

 

Prefabs 폴더에 있는 hitParticle을 Ctrl + D를 눌러서 복사합니다 

그리고 이름을 DethParticle이라고 합니다 

 

DethParticle의 Material을 red로 바꿉니다 

 

하이 라키 뷰에 GameOver, Success 텍스쳐를 Disable(안 보이게)합니다 

 

Scripts폴더에 DeathZone 스크립트를 만들고 작성합니다 

 

 

 

 

 

 

 

DeathZone 스크립트 작성

 

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

public class DeathZone : MonoBehaviour
{
    private void OnTriggerEnter()
    {
        GameManager.Instance.LoseLife();
    }
}

 

하이 라키 뷰에서  화면 맨 아래 있는 오브젝트인 Cube (3)을 선택하여 DeathZone 스크립트를 붙입니다 그리고 

is Trigger를 체크합니다 

 

 

하이 라키 뷰에서 GameManager 오브젝트를 선택하고 아래 그림과 같이 속성을 바꾸고 텍스쳐와 프리 팹을 연결합니다 

 

 

 

 

게임을 실행하여 정상으로 게임이 잘되는지 확인합니다 

 

 

작업 파일을 올려놓습니다 

참고자료가 조그 미나 마 도움이 되었으면 합니다 

부자 되시고 항상 복 받으세요 ㅎㅎ

BreakGame_Export3.unitypackage
0.06MB

 

반응형
반응형

 

하이 라키 뷰에 있는 Paddler과 Sphere를 동시에 잡고  Rigidbody  컴포넌트를 추가합니다

그리고 IsKinematic을 체크합니다

 

BallCtrl 스크립트를 생성하고 스크립트를 작성합니다 

 

 

 

BallCtrl 스크립트 작성

 

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

public class BallCtrl : MonoBehaviour
{
    //볼의 가속 속도
    public float BallInitialVelocity = 300f;

    //리지드 바디
    private Rigidbody ballRigidBody = null;

    //볼플레이 선택여부
    private bool isBallInPlay = false;

    private void Awake()
    {
        ballRigidBody = GetComponent<Rigidbody>();
    }
    void Update()
    {
        //마우스 오른쪽 키를 누르면 볼에 가속도를 준다 
        if (Input.GetButtonDown("Fire1") && !isBallInPlay)
        {
            transform.parent = null;
            isBallInPlay = true;
            ballRigidBody.isKinematic = false;
            ballRigidBody.AddForce(new Vector3(BallInitialVelocity, BallInitialVelocity, 0f));
        }
    }
}

 

 

BallCtrl 스크립트를 Sphere 오브젝트에 붙입니다 

 

 

게임을 실행시키면 마우스 오른쪽 버튼을 누르면 구슬이 튕겨져 블록으로 나가는 모습을 볼 수 있습니다 

 

반응형
반응형

아래 그림처럼 벽돌을 깨는 고전 게임을 만들어 보겠습니다 

옛날에 알카노이드 란 이름으로 나왔던 게임을 조금 흉내 내어 보겠습니다 

 

게임 화면을 576 : 1024 로 맞춥니다 

 

 

 

하이라키뷰에 있는 메인 카메라를 선택하고  Projection -> orthographic 으로 설정합니다

 

Assets에  materials폴더를 만들고 Create -> Physic Material을 선택하여 생성합니다 

 

 

 

Physic Material 의 속성을 아래 그림과 같이 바꿉니다

 

하면에 4개의 큐브를 만들 과 화면 태 투리에 큐브가 보일 정도로 위치를 합니다 

 

게임 화면에서 보일 때 밑에 있는 큐브는 약간 안 보이게 합니다 

 

 

 

네 개의 큐브를 선택하고  Box Collider -> Material에 있는 부분을 new Physic Material을 드래그하여 올려놓습니다

 

 

그리고 하이 라키 뷰에 빈 오브젝트를 만들고 이름을 block이라고 하고 자식으로 3D Object ->Cube를 32 개를 생성하여 아래 그림과 같이 4개의 큐브 안에 들어오게 하여 정렬을 시킵니다 

 

32개의 큐브를 선택하고  Box Collider -> Material에 있는 부분을 new Physic Material을 드래그하여 올려놓습니다

 

 

 

block 오브젝트를 선택하여 Prefabs 폴더를 만들어 거기에 드래그하여 프리 팹을 만듭니다

하이 라키 뷰에 3D Object ->Cube를 생성하고 이름을 Paddle로 하고 Transform을 아래 그림과 같이 하고  Box Collider -> Material에 있는 부분을 new Physic Material을 드래그하여 올려놓습니다

 

하이 라키 뷰에3D Object ->Sphere를 생성하고  Transform을 아래 그림과 같이 하고  Box Collider -> Material에 있는 부분을 new Physic Material을 드래그하여 올려놓습니다

 

Sphere를 Paddle의 자식으로 놓습니다

 

 

 

Paddle을 움직일 PaddleCtrl 스크립트를 생성하여 스크립트를 작성합니다

 

PaddleCtrl 스크립트 작성

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

public class PaddleCtrl : MonoBehaviour
{
    public float PaddleSpeed = 1f;//Paddle의 이동 스피드
    private Vector3 playerPos = new Vector3(0f, -3.5f, 0f);//Paddle의 초기 위치

    private void Update()
    {
        //Paddle 좌우 이동
        float xPos = transform.position.x + (Input.GetAxis("Horizontal") * PaddleSpeed);

        // Paddle 에이동 제한을 준다 
        playerPos = new Vector3(Mathf.Clamp(xPos, -2f, 2f), -3.5f, 0f);
        transform.position = playerPos;
    }
}

 

 

 

 

Materials 폴더를 선택하고 Create -> Material  두 개 만듭니다 

 

 

 

green 메터리얼은 Paddle에 붙이고 red는 Sphere에 붙힘니다 

 

작성한 PaddleCtrl을 Paddle 오브젝트에 붙히고 Paddle 오브젝트를 프리 팹으로 만듭니다

 

 

 

 

 

게임을 실행하면 Paddle  좌우 키를 움직일 때 방향대로 Paddle 이 움직이는 것을 볼 수 있습니다

 

반응형
반응형

유니티 UI를 활용하여 게임 시간과 그림 맞춤 카드 횟수 및 스테이지 시간을 화면에 출력하겠습니다

먼저 하이라키 뷰에서 오른쪽 마우스 키를 누르고 UI ->Text를 아래 그림과 같이 5개 생성합니다 

 

 

 

아래 화면과 같이 텍스트를 이동합니다 보통 텍스트 폰트를 변경하고 알맞은 색상을 선택하여야 하는데 그 부분은 생략하고 대략 텍스트 위치와 화면에 나타내는 것만 보이도록 하겠습니다

 

 

GameManager 스크립트를 수정합니다

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

public class GameManager : MonoBehaviour
{

    //게임 전체 시간, 스테이지 시간 , 스테이지 번호 , 맞춘 카드 의 숫자 
    public Text totalTimeText, stageTimeText, stageText, hitText, stageNumText;

    //클릭한 카드 번호
    static public int cardNum;

    //직전의 카드 번호
    int lastNum = 0;

    //스테이지의 전체 카드수
    int cardCnt;

    //카드 클릭 횟수
    int hitCnt = 0;

    //스테이지 번호
    static public int stageNum = 1;

    //스테이지 수
    int stageCnt = 6;

    //카드 배열 카드 섞기용
    int[] arCards = new int[50];

    //게임 시작 시간
    float startTime;

    // 스테이지 경과 시간
    float stageTime;

    //열거형 의 자료를 설정하는 정의문 나열의 자료는 0,1,2....의 값이 할당된다
    public enum STATE
    {
        START, HIT, WAIT, IDLE, CLEAR
    };

    static public STATE state = STATE.START;

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

        
        Screen.orientation = ScreenOrientation.LandscapeRight;
        Screen.sleepTimeout = SleepTimeout.NeverSleep;

        startTime = stageTime = Time.time;

        //시간초기화
        startTime = stageTime = Time.time;
        //스테이지 만들기
        // StartCoroutine(MakeStage());
    }

    void Update()
    {

        int time1 = (int)(Time.time - startTime);
        int time2 = (int)(Time.time - stageTime);

        //게임 전체 시간, 스테이지 시간 , 스테이지 번호 , 맞춘 카드 의 숫자 를 화면에 출력한다
        totalTimeText.text = "Total time : " + time1;
        stageTimeText.text = "Stage Time : " + time2;
        stageText.text = "Stage : " + stageNum;
        hitText.text = "Hit : " + hitCnt;

        switch (state)
        {
            //스테이지 만들기
            case STATE.START:
                StartCoroutine(MakeStage());
                break;

                // 같은 그림인지 판정
            case STATE.HIT:

                CheckCard();
                break;

                //스테이지를 클리어 하고 다음 스테이지를 만듬
            case STATE.CLEAR:
                StartCoroutine(StageClear());
                break;

        }

        //게임중지
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }

    }

    void CheckCard()
    {
        state = STATE.WAIT;

        //첫 번째 카드
        if (lastNum == 0)
        {
            //현재 카드 보존
            lastNum = cardNum;
            state = STATE.IDLE;
            return;
        }

        //이미지 찾기
        int img1 = (cardNum + 1) / 2;
        int img2 = (lastNum + 1) / 2;

        //다른 카드 이면  카드를 닫게한다
        if (img1 != img2)
        {
            StartCoroutine(CloseTwoCards());

            lastNum = 0;
            state = STATE.IDLE;
            return;
        }


        //같은 카드면 
        hitCnt += 2;

        //카드가 모두 열리면 스테이지를 클리어 한다
        if (hitCnt == cardCnt)
        {
            state = STATE.CLEAR;
            return;
        }

        //카드가 남아있으면 다른 카드를 조사한다
        lastNum = 0;
        state = STATE.IDLE;
    }

    //카드 닫기
    IEnumerator CloseTwoCards()
    {
        //테그로 카드 찾기
        GameObject card1 = GameObject.FindWithTag("card" + lastNum);
        GameObject card2 = GameObject.FindWithTag("card" + cardNum);


        //카드 닫기
        yield return new WaitForSeconds(0.2f);
        card1.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);
        card2.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);

    }

    //스테이지를 클리어 한다
    IEnumerator StageClear()
    {
        state = STATE.WAIT;

        yield return new WaitForSeconds(2);


        //스테이지 카드 제거
        for (int i = 1; i <= cardCnt; i++)
        {
            GameObject card = GameObject.FindWithTag("card" + i);
            Destroy(card);
        }

        //다음 스테이지 번호
        ++stageNum;
        if (stageNum > stageCnt)
        {
            Application.LoadLevel("GameStart");
            //return;
        }

        //스테이지 초기화
        stageTime = Time.time;
        lastNum = 0;
        hitCnt = 0;

        state = STATE.START;
    }
    IEnumerator MakeStage()
    {
        //현재 작업중으로 설정
        state = STATE.WAIT;

        StartCoroutine(ShowStageNum());

        //시작카드의 x좌표
        float sx = 0;

        //시작카드의 z좌표
        float sz = 0;

        
        SetCardPos(out sx, out sz);

        //카드섞기
        ShuffleCard();


        //시작카드의 번호
        int n = 1;

        //카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
        string[] str = StageSet.stage[stageNum - 1];

        //배열의 행의 수만큼 반복
        foreach (string t in str)
        {
            // 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
            char[] ch = t.Trim().ToCharArray();

            //카드의 x축 좌표
            float x = sx;

            //1행의 문자열 길이만큼 반복
            //배열의 ch의 한문자를 읽고 변수 c에 할당한다
            foreach (char c in ch)
            {
                switch (c)
                {

                    //맵의 내용이 * 이면 그위치에 카드 만들어서 배치
                    case '*':
                        //카드 만들기
                        GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;

                        //카드 좌표설정
                        card.transform.position = new Vector3(x, 0, sz);

                        //태그 달기
                        //card.tag = "card" + n++;

                        
                        //섞인카드
                        card.tag = "card" + arCards[n++];
                        x++;
                        break;

                        //빈칸 처리
                    case '.':
                        x++;
                        break;


                        //반 칸 공백처리
                    case '>':
                        x += 0.5f;
                        break;


                        //반 줄 행간 처리
                    case '^':
                        sz += 0.5f;
                        break;
                }

                //카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
                if (c == '*')
                {
                    yield return new WaitForSeconds(0.03f);

                }
            }

            //한줄 아래로 이동
            sz--;
        }
        //입력 대기중으로 설정
        state = STATE.IDLE;
    }

    //카드의 시작 위치 계산
    void SetCardPos(out float sx, out float sz)
    {
        //가로 카드수 반 칸 공백 포함
        float x = 0;

        //세로 행수 반줄 행간 포함
        float z = 0;

        //가로 카드 최대수
        float maxX = 0;

        //스테이지 전체 카드수
        cardCnt = 0;

        //카드 배열 조사 맵 배열 을 읽음
        string[] str = StageSet.stage[stageNum - 1];

        //행의 수만큼 반복
        for (int i = 0; i < str.Length; i++)
        {
            //1행읽기
            string t = str[i].Trim();

            //각행의 카드수
            x = 0;

            //각행의 글자 수만큼 반복
            for (int j = 0; j < t.Length; j++)
            {
                //문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
                switch (t[j])
                {
                    case '.':
                    case '*':

                        //카드배치에 필요한 공간
                        x++;
                        if (t[j] == '*')
                        {
                            //전체 카드수
                            cardCnt++;
                        }
                        break;
                    case '>':
                        x += 0.5f;
                        break;
                    case '^':
                        z -= 0.5f;
                        break;

                }
            }

            //각 행의 최대 카드수 계산
            if (x > maxX)
            {
                maxX = x;
            }
            //전체 행의 수
            z++;
        }
        //카드 가로 시작 위치
        sx = -maxX / 2;
        sz = (z - 1) / 2;

        //  StartCoroutine(CardOpen(cardCnt));
    }

    void ShuffleCard()
    {
        for (int i = 1; i <= cardCnt; i++)
        {
            arCards[i] = i;
        }
        //return;

        //카드섞기 15회정도 반복
        for (int i = 1; i <= 15; i++)
        {
            //임의의 난수를 두개 만든다
            int n1 = Random.Range(1, cardCnt + 1);
            int n2 = Random.Range(1, cardCnt + 1);

            //교환
            int t = arCards[n1];//배열의 값을 바꾼다
            arCards[n1] = arCards[n2];
            arCards[n2] = t;

            //StartCoroutine(CardOpen(arCards[n1]));
        }
    }

    //스테이지 시작시 스테이지 번호를 보여준다
    IEnumerator ShowStageNum()
    {
        stageNumText.text = "STATE " + stageNum;

        yield return new WaitForSeconds(2f);

        stageNumText.text = "";
    }
}

 

 

 

 

GameManager 스크립트를 선택하고  하이라키 뷰에서 생성한 텍스트를 아래 그림과 같이 드래그하여 붙입니다

그리고 CardCtrl 스크립트를 선택하고 수정합니다

 

CardCtrl 스크립트 수정

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

public class CardCtrl : MonoBehaviour
{
    //이미지 번호
    int imgNum = 1;

    //카드 뒷면 이미지 번호
    int backNum = 1;

    // 오픈된 카드의 판별여부 
    bool isOpen = false;

    Animator anim;


    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();

        CardView();
    }
    
    //스테이지 첫 시작할때 전체 그림을 보여준다
    void CardView()
    {
        int cardNum = int.Parse(transform.tag.Substring(4));

        imgNum = (cardNum + 1) / 2;
        anim.Play("aniOpen");

        GameManager.cardNum = cardNum;

        StartCoroutine(CardCloseStart());
    }
    IEnumerator CardCloseStart()
    {
        yield return new WaitForSeconds(2f);
        anim.Play("aniClose");
    }


    void Update()
    {
        //왼쪽 마우스버튼 클릭 모바일에선 터치 
        if (Input.GetButtonDown("Fire1") && GameManager.state == GameManager.STATE.IDLE)
        {
            CheckCard();
        }
    }

    //카드체크
    void CheckCard()
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        //터치한 카드 식별
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            string tag = hit.transform.tag;
            if (tag.Substring(0, 4) == "card")
            {
                //터치한 카드의 OpenCard()함수 실행
                hit.transform.SendMessage("OpenCard", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    void OpenCard()
    {
        //열린 카드는 처리 없음
        if (isOpen) return;

        //열린 카드 참거짓 판정
        isOpen = true;

        // 카드 번호 Substring() 문자열의 일부분을 추출하는 함수 , 카드 번호(tag)는 card0~card32 있으므로 문자4번째부터(card~) 끝까지 추출한다
        int cardNum = int.Parse(transform.tag.Substring(4));

        //이미지 번호 카드 두장에 하나씩 같은 이미지를 할당하므로 이미지 번호는 (카드번호 +1)/2 로 구한다 정수 /정수 = 정수 이므로 소수 이하는 자동으로 잘림
        imgNum = (cardNum + 1) / 2;
        //카드 애니메이션 실행
        anim.Play("aniOpen");

        GameManager.cardNum = cardNum;
        GameManager.state = GameManager.STATE.HIT;

    }
    void CloseCard()
    {
        anim.Play("aniClose");
        isOpen = false;
    }
    //카드 앞면을 이미지을 가지고 온다
    void ShowImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
    }


    //카드 뒷면 이미지을 가지고 온다
    void HideImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
    }
}

 

 

 

Main Camera 오브젝트를 선택하고 Clear Flags -> Depth only로 바꾸고  Depth 값을 1 합니다

 

그리고 MaskCamera를 선택하고 Active를 체크합니다

 

 

게임 씬 이름을 MainGame으로 바꾸고 

GameStart 신을 새로 생성합니다 

그리고 GameStart 씬으로 들어갑니다

MainCamera를 선택하고 Clear Flages -> Solid Color로 바꿉니다

 

 

Button을 생성하여 게임 스타트 이름을 하고 text를 생성하여 두니 회전 카드 게임 이란 타이틀을 넣습니다 

 

GameStart 스크립트를 생성합니다 

 

GameStart 스크립트 작성

 

 

GameStart 스크립트를  Button에 붙이고 On Click()에서 GameStart -> GameStartScene() 함수를 연결합니다 

 

File -> Build Settings를 들어가서 폴더 Scenes에 있는 GameStart와 MainGame을 드래그하여 Build Settings에 올립니다

 

 

 

게임을 실행하여 텍스트와 게임이 잘 진행되는지 확인합니다 

 

 

반응형
반응형

구글 스토어 다운로드

 

전 세계가 초토화 되고 좀비들의 습격이 시작됩니다
미친 좀비들이 사정없이 공격하여 옵니다
쳐들어 오는 좀비를 철조망이 무너지지 않게 좀비들을 무찌릅니다
좀비들을 없애면 랜덤으로 골드가 생성됩니다
생성된 골드를 획득하고 메인 요새 화면으로 돌아가 캐릭터를 강력하게 할수 있는 무기를 구매합니다
총알도 강력하게 업그래드 할 총알을 구매할수 있습니다
무기를 구매하여 포켓 지갑에 넣고 인간 요새를 지키기 위해 철조먕을 사이에 두고 좀비들을 무찌릅니다
스테이지가 올라갈수록 좀비들의 숫자가 늘어나고 좀비들의 체력이 올라 감니다
그리고 강력한 좀비 보스들이 출현 합니다
확끈하게 좀비들을 쳐부술수 있는 무기를 구해하여 좀비들을 없애고 스테이지를 올려 점점 강력한 좀비들과 대결을 하세요
지구의 운명이 당신에게 있습니다

 

 

무시무시한 좀비들의 무지막지한 공격력 

 

 

확끈하게 캐릭터 주인공의 무기를 업그래이드 시켜서 좀비들을 막아냅니다 

공포의 좀비들을 파괴적인 액션 무기를 장착하여 좀비을 무찌르자 

 

 

다양하게 장착할수 있는 군용 무기를 창착할수 있습니다 

구글스토어  게임 다운로드 주소

https://play.google.com/store/apps/details?id=com.Magatron.WorldWarX

반응형
반응형

카드 판정에 따른 게임의 상태를 표시하고 카드가 모두 열리면 다음 스테이지로 이동하는 프로그램을 만들어 보겠습니다

먼제 GameManager 스크립트를 열고 수정합니다

 

GameManager 스크립트 수정

게임의 상태를 설정할 수 있는 enum 열거형의 자료를 설정하고 Star() 함수에 모바일 기기를 위한 설정을 추가합니다

초기의 스테이지 만드는 문장도 삭제합니다

그리고 Update() 함수에 state 값을 바꾸어서 반복 호출하지 않도록 MakeStage() 함수에 처음 state = STATE.WAIT을 추가하고 마지막에 state = STATE.IDLE을 추가합니다.

 

또한 카드 판정할 수 있는 CheckCard() 함수를 추가하여  카드를 판정하고 카드가 서로 다르면 CloseTwoCards() 함수를 추가하여 카드를 닫습니다

그래서 스테이지 카드가 모두 열리면 StageClear() 함수를 추가하여 스테이지를 클리어하고 다음 스테이지로 넘어가는 함수를 만듭니다

 

 

 

GameManager 스크립트 수정

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

public class GameManager : MonoBehaviour
{
    //클릭한 카드 번호
    static public int cardNum;

    //직전의 카드 번호
    int lastNum = 0;

    //스테이지의 전체 카드수
    int cardCnt;

    //카드 클릭 횟수
    int hitCnt = 0;

    //스테이지 번호
    static public int stageNum = 1;

    //스테이지 수
    int stageCnt = 6;

    //카드 배열 카드 섞기용
    int[] arCards = new int[50];

    //게임 시작 시간
    float startTime;

    // 스테이지 경과 시간
    float stageTime;

    //열거형 의 자료를 설정하는 정의문 나열의 자료는 0,1,2....의 값이 할당된다
    public enum STATE
    {
        START, HIT, WAIT, IDLE, CLEAR
    };

    static public STATE state = STATE.START;

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

        
        Screen.orientation = ScreenOrientation.LandscapeRight;
        Screen.sleepTimeout = SleepTimeout.NeverSleep;

        startTime = stageTime = Time.time;

        //시간초기화
        startTime = stageTime = Time.time;
        //스테이지 만들기
        // StartCoroutine(MakeStage());
    }

    void Update()
    {
        switch (state)
        {
            //스테이지 만들기
            case STATE.START:
                StartCoroutine(MakeStage());
                break;

                // 같은 그림인지 판정
            case STATE.HIT:

                CheckCard();
                break;

                //스테이지를 클리어 하고 다음 스테이지를 만듬
            case STATE.CLEAR:
                StartCoroutine(StageClear());
                break;

        }

        //게임중지
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }

    }

    void CheckCard()
    {
        state = STATE.WAIT;

        //첫 번째 카드
        if (lastNum == 0)
        {
            //현재 카드 보존
            lastNum = cardNum;
            state = STATE.IDLE;
            return;
        }

        //이미지 찾기
        int img1 = (cardNum + 1) / 2;
        int img2 = (lastNum + 1) / 2;

        //다른 카드 이면  카드를 닫게한다
        if (img1 != img2)
        {
            StartCoroutine(CloseTwoCards());

            lastNum = 0;
            state = STATE.IDLE;
            return;
        }


        //같은 카드면 
        hitCnt += 2;

        //카드가 모두 열리면 스테이지를 클리어 한다
        if (hitCnt == cardCnt)
        {
            state = STATE.CLEAR;
            return;
        }

        //카드가 남아있으면 다른 카드를 조사한다
        lastNum = 0;
        state = STATE.IDLE;
    }

    //카드 닫기
    IEnumerator CloseTwoCards()
    {
        //테그로 카드 찾기
        GameObject card1 = GameObject.FindWithTag("card" + lastNum);
        GameObject card2 = GameObject.FindWithTag("card" + cardNum);


        //카드 닫기
        yield return new WaitForSeconds(0.2f);
        card1.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);
        card2.SendMessage("CloseCard", SendMessageOptions.DontRequireReceiver);

    }

    //스테이지를 클리어 한다
    IEnumerator StageClear()
    {
        state = STATE.WAIT;

        yield return new WaitForSeconds(2);


        //스테이지 카드 제거
        for (int i = 1; i <= cardCnt; i++)
        {
            GameObject card = GameObject.FindWithTag("card" + i);
            Destroy(card);
        }

        //다음 스테이지 번호
        ++stageNum;
        if (stageNum > stageCnt)
        {
            //Application.LoadLevel("GameStart");
            //return;
        }

        //스테이지 초기화
        stageTime = Time.time;
        lastNum = 0;
        hitCnt = 0;

        state = STATE.START;
    }
    IEnumerator MakeStage()
    {
        //현재 작업중으로 설정
        state = STATE.WAIT;

        //시작카드의 x좌표
        float sx = 0;

        //시작카드의 z좌표
        float sz = 0;

        
        SetCardPos(out sx, out sz);

        //카드섞기
        ShuffleCard();


        //시작카드의 번호
        int n = 1;

        //카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
        string[] str = StageSet.stage[stageNum - 1];

        //배열의 행의 수만큼 반복
        foreach (string t in str)
        {
            // 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
            char[] ch = t.Trim().ToCharArray();

            //카드의 x축 좌표
            float x = sx;

            //1행의 문자열 길이만큼 반복
            //배열의 ch의 한문자를 읽고 변수 c에 할당한다
            foreach (char c in ch)
            {
                switch (c)
                {

                    //맵의 내용이 * 이면 그위치에 카드 만들어서 배치
                    case '*':
                        //카드 만들기
                        GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;

                        //카드 좌표설정
                        card.transform.position = new Vector3(x, 0, sz);

                        //태그 달기
                        //card.tag = "card" + n++;

                        
                        //섞인카드
                        card.tag = "card" + arCards[n++];
                        x++;
                        break;

                        //빈칸 처리
                    case '.':
                        x++;
                        break;


                        //반 칸 공백처리
                    case '>':
                        x += 0.5f;
                        break;


                        //반 줄 행간 처리
                    case '^':
                        sz += 0.5f;
                        break;


                }

                //카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
                if (c == '*')
                {
                    yield return new WaitForSeconds(0.03f);

                }
            }

            //한줄 아래로 이동
            sz--;
        }
        //입력 대기중으로 설정
        state = STATE.IDLE;
    }

    //카드의 시작 위치 계산
    void SetCardPos(out float sx, out float sz)
    {
        //가로 카드수 반 칸 공백 포함
        float x = 0;

        //세로 행수 반줄 행간 포함
        float z = 0;

        //가로 카드 최대수
        float maxX = 0;

        //스테이지 전체 카드수
        cardCnt = 0;

        //카드 배열 조사 맵 배열 을 읽음
        string[] str = StageSet.stage[stageNum - 1];

        //행의 수만큼 반복
        for (int i = 0; i < str.Length; i++)
        {
            //1행읽기
            string t = str[i].Trim();

            //각행의 카드수
            x = 0;

            //각행의 글자 수만큼 반복
            for (int j = 0; j < t.Length; j++)
            {
                //문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
                switch (t[j])
                {
                    case '.':
                    case '*':

                        //카드배치에 필요한 공간
                        x++;
                        if (t[j] == '*')
                        {
                            //전체 카드수
                            cardCnt++;
                        }
                        break;
                    case '>':
                        x += 0.5f;
                        break;
                    case '^':
                        z -= 0.5f;
                        break;

                }
            }

            //각 행의 최대 카드수 계산
            if (x > maxX)
            {
                maxX = x;
            }
            //전체 행의 수
            z++;
        }
        //카드 가로 시작 위치
        sx = -maxX / 2;
        sz = (z - 1) / 2;

        //  StartCoroutine(CardOpen(cardCnt));
    }

    void ShuffleCard()
    {
        for (int i = 1; i <= cardCnt; i++)
        {
            arCards[i] = i;
        }
        //return;

        //카드섞기 15회정도 반복
        for (int i = 1; i <= 15; i++)
        {
            //임의의 난수를 두개 만든다
            int n1 = Random.Range(1, cardCnt + 1);
            int n2 = Random.Range(1, cardCnt + 1);

            //교환
            int t = arCards[n1];//배열의 값을 바꾼다
            arCards[n1] = arCards[n2];
            arCards[n2] = t;

            //StartCoroutine(CardOpen(arCards[n1]));
        }
    }
}

 

 

 

카드와 게임 메니져 GameManager 스크립트와 통신하기 위해서 CardCtrl 스크립트를 수정합니다

 

CardCtrl 스크립트 수정

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

public class CardCtrl : MonoBehaviour
{
    //이미지 번호
    int imgNum = 1;

    //카드 뒷면 이미지 번호
    int backNum = 1;

    // 오픈된 카드의 판별여부 
    bool isOpen = false;

    Animator anim;


    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        //왼쪽 마우스버튼 클릭 모바일에선 터치 
        if (Input.GetButtonDown("Fire1") && GameManager.state == GameManager.STATE.IDLE)
        {
            CheckCard();
        }
    }

    //카드체크
    void CheckCard()
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        //터치한 카드 식별
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            string tag = hit.transform.tag;
            if (tag.Substring(0, 4) == "card")
            {
                //터치한 카드의 OpenCard()함수 실행
                hit.transform.SendMessage("OpenCard", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    void OpenCard()
    {
        //열린 카드는 처리 없음
        if (isOpen) return;

        //열린 카드 참거짓 판정
        isOpen = true;

        // 카드 번호 Substring() 문자열의 일부분을 추출하는 함수 , 카드 번호(tag)는 card0~card32 있으므로 문자4번째부터(card~) 끝까지 추출한다
        int cardNum = int.Parse(transform.tag.Substring(4));

        //이미지 번호 카드 두장에 하나씩 같은 이미지를 할당하므로 이미지 번호는 (카드번호 +1)/2 로 구한다 정수 /정수 = 정수 이므로 소수 이하는 자동으로 잘림
        imgNum = (cardNum + 1) / 2;
        //카드 애니메이션 실행
        anim.Play("aniOpen");

        GameManager.cardNum = cardNum;
        GameManager.state = GameManager.STATE.HIT;

    }
    void CloseCard()
    {
        anim.Play("aniClose");
        isOpen = false;
    }
    //카드 앞면을 이미지을 가지고 온다
    void ShowImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
    }


    //카드 뒷면 이미지을 가지고 온다
    void HideImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
    }
}

 

 

 

 

임포트 받았더 back5 를 back1으로 수정합니다 

 

게임을 실행시켜서 두 개의 그림이 맞으면 서로 열리고 안 맞으면 서로 닫힙니다 

그리고 그림이 모두 열리면 다음 스테이지로 넘어갑니다 

아래 동영상과 같이 게임이 실행되면 성공입니다

 

 

반응형
반응형

카드를 Touch 및 클릭하여 카드를 뒤집고 카드를 리소스의 순번대로 나오는 것을 랜덤으로 섞는 프로그램을 만들어 보겠습니다

먼저 CardCtrl 스크립트를 열고 수정합니다

 

 

 

 

CardCtrl 스크립트 수정

Update()와 CheckCard() 함수 부분을 수정 및 추가합니다

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

public class CardCtrl : MonoBehaviour
{
    //이미지 번호
    int imgNum = 1;

    //카드 뒷면 이미지 번호
    int backNum = 1;

    // 오픈된 카드의 판별여부 
    bool isOpen = false;

    Animator anim;


    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();
    }

    void Update()
    {
        //왼쪽 마우스버튼 클릭 모바일에선 터치 
        if (Input.GetButtonDown("Fire1") )
        {
            CheckCard();
        }
    }

    //카드체크
    void CheckCard()
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        //터치한 카드 식별
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            string tag = hit.transform.tag;
            if (tag.Substring(0, 4) == "card")
            {
                //터치한 카드의 OpenCard()함수 실행
                hit.transform.SendMessage("OpenCard", SendMessageOptions.DontRequireReceiver);
            }
        }
    }
    void OpenCard()
    {
        if (isOpen) return;
        isOpen = true;

        // 카드 번호 Substring() 문자열의 일부분을 추출하는 함수 , 카드 번호(tag)는 card0~card32 있으므로 문자4번째부터(card~) 끝까지 추출한다
        int cardNum = int.Parse(transform.tag.Substring(4));

        //이미지 번호 카드 두장에 하나씩 같은 이미지를 할당하므로 이미지 번호는 (카드번호 +1)/2 로 구한다 정수 /정수 = 정수 이므로 소수 이하는 자동으로 잘림
        imgNum = (cardNum + 1) / 2;
        //카드 애니메이션 실행
        anim.Play("aniOpen");

       //GameManager.cardNum = cardNum;
        //GameManager.state = GameManager.STATE.HIT;

    }
    void CloseCard()
    {
        anim.Play("aniClose");
        isOpen = false;
    }
    //카드 앞면을 이미지을 가지고 온다
    void ShowImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
    }


    //카드 뒷면 이미지을 가지고 온다
    void HideImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
    }
}

 

 

 

 

아래 동영상과 카드를 터치하였을 때 카드가 열립니다 그런데 카드가 섞이지 않고 순번대로 열립니다 

 

 

 

 

GameManager 스크립트 수정합니다

 

 

 

GameManager 스크립트 수정

카드를 섞는 함수를 작성

ShuffleCard() 함수 작성 및 카드 태그 인식 부분 수정

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

public class GameManager : MonoBehaviour
{
    //클릭한 카드 번호
    static public int cardNum;

    //직전의 카드 번호
    int lastNum = 0;

    //스테이지의 전체 카드수
    int cardCnt;

    //카드 클릭 횟수
    int hitCnt = 0;

    //스테이지 번호
    static public int stageNum = 1;

    //스테이지 수
    int stageCnt = 6;

    //카드 배열 카드 섞기용
    int[] arCards = new int[50];

    //게임 시작 시간
    float startTime;

    // 스테이지 경과 시간
    float stageTime;


    // Start is called before the first frame update
    void Start()
    {
        //시간초기화
        startTime = stageTime = Time.time;
        //스테이지 만들기
         StartCoroutine(MakeStage());
    }

    void Update()
    {

    }

    IEnumerator MakeStage()
    {
        //시작카드의 x좌표
        float sx = 0;

        //시작카드의 z좌표
        float sz = 0;

        
        SetCardPos(out sx, out sz);

        //카드섞기
        ShuffleCard();


        //시작카드의 번호
        int n = 1;

        //카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
        string[] str = StageSet.stage[stageNum - 1];

        //배열의 행의 수만큼 반복
        foreach (string t in str)
        {
            // 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
            char[] ch = t.Trim().ToCharArray();

            //카드의 x축 좌표
            float x = sx;

            //1행의 문자열 길이만큼 반복
            //배열의 ch의 한문자를 읽고 변수 c에 할당한다
            foreach (char c in ch)
            {
                switch (c)
                {

                    //맵의 내용이 * 이면 그위치에 카드 만들어서 배치
                    case '*':
                        //카드 만들기
                        GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;

                        //카드 좌표설정
                        card.transform.position = new Vector3(x, 0, sz);

                        //태그 달기
                        //card.tag = "card" + n++;

                        
                        //섞인카드
                        card.tag = "card" + arCards[n++];
                        x++;
                        break;

                        //빈칸 처리
                    case '.':
                        x++;
                        break;


                        //반 칸 공백처리
                    case '>':
                        x += 0.5f;
                        break;


                        //반 줄 행간 처리
                    case '^':
                        sz += 0.5f;
                        break;


                }

                //카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
                if (c == '*')
                {
                    yield return new WaitForSeconds(0.03f);

                }
            }

            //한줄 아래로 이동
            sz--;
        }

    }

    //카드의 시작 위치 계산
    void SetCardPos(out float sx, out float sz)
    {
        //가로 카드수 반 칸 공백 포함
        float x = 0;

        //세로 행수 반줄 행간 포함
        float z = 0;

        //가로 카드 최대수
        float maxX = 0;

        //스테이지 전체 카드수
        cardCnt = 0;

        //카드 배열 조사 맵 배열 을 읽음
        string[] str = StageSet.stage[stageNum - 1];

        //행의 수만큼 반복
        for (int i = 0; i < str.Length; i++)
        {
            //1행읽기
            string t = str[i].Trim();

            //각행의 카드수
            x = 0;

            //각행의 글자 수만큼 반복
            for (int j = 0; j < t.Length; j++)
            {
                //문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
                switch (t[j])
                {
                    case '.':
                    case '*':

                        //카드배치에 필요한 공간
                        x++;
                        if (t[j] == '*')
                        {
                            //전체 카드수
                            cardCnt++;
                        }
                        break;
                    case '>':
                        x += 0.5f;
                        break;
                    case '^':
                        z -= 0.5f;
                        break;

                }
            }

            //각 행의 최대 카드수 계산
            if (x > maxX)
            {
                maxX = x;
            }
            //전체 행의 수
            z++;
        }
        //카드 가로 시작 위치
        sx = -maxX / 2;
        sz = (z - 1) / 2;

        //  StartCoroutine(CardOpen(cardCnt));
    }

    void ShuffleCard()
    {
        for (int i = 1; i <= cardCnt; i++)
        {
            arCards[i] = i;
        }
        //return;

        //카드섞기 15회정도 반복
        for (int i = 1; i <= 15; i++)
        {
            //임의의 난수를 두개 만든다
            int n1 = Random.Range(1, cardCnt + 1);
            int n2 = Random.Range(1, cardCnt + 1);

            //교환
            int t = arCards[n1];//배열의 값을 바꾼다
            arCards[n1] = arCards[n2];
            arCards[n2] = t;

            //StartCoroutine(CardOpen(arCards[n1]));
        }
    }
}

 

 

 

 

아래 동영상과 같이 카드가 일렬 배열대로 안 나오고 섞어서 나옵니다

 

반응형
반응형

게임 스테이지마다 큐브로 만든 카드를 배열하여 맵을 만들어 보겠습니다

 

먼저 card0~card32까지 태그를 만듬니다

 

맵은 별도의 텍스트 파일로 만든 후 게임 시작 시 그것을 불러와서 처리하도록 할 수 있지만, 절차가 복잡하므로 맵을 2차원 가변 배열로 만듭니다. C#의 2차원 가변 배열은 다음과 같은 형식으로 만듭니다

 static public string [][] stage = new string[][]

{

    new string[] {"문자열, "문자열", },  //스테이지 1

  new string [] {"문자열, "문자열", },  //스테이지 2

   new string [] {"문자열, "문자열", }, //스테이지 3

}; //배열의 끝

 

StageSet 스크립트를 생성하고 작성합니다

 

 

 

 

StageSet 스크립트 작성

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

public class StageSet : MonoBehaviour
{
    //스테이지 2차원 배열을 만든다
    static public string[][] stage = new string[][]
       {

        new string[]
        {
            "   .*..*.   ",
            "   .****.   ",
            "   ******   ",
            "   .****.   ",
            "   .*..*.   "

        },

        new string[]
        {
            "   .*...*.   ",
            "   >**..**.  ",
            "   ***.***   ",
            "   >**..**   ",
            "   .*...*.   "
        },

        new string[]
        {
            "   **....**   ",
            "   **.**.**   ",
            " ^ ..*..*..   ",
            " ^ **.**.**   ",
            "   **....**   "
        },

        new string[]
        {
            "   ...**...   ",
            " ^ ..*..*..   ",
            " ^ .*.**.*.   ",
            " ^ ..*..*..   ",
            " ^ **.**.**   ",
            " ^ ..*..*..   ",
            " ^ .*.**.*.   ",
            " ^ ..*..*..   ",
            "   ...**...   ",
        },

        new string[]
        {
            "   .*.>*.>*   ",
            "   **.**.**   ",
            " ^ ..*..*..   ",
            " ^ **.**.**   ",
            " ^ ..*..*..   ",
            " ^ .*.**.*.   ",
            "   .*.>*.>*   ",
        },

        new string[]
        {
            "    .**...**.   ",
            "   > ***..***.  ",
            "    ****.****   ",
            "   > ***..***.  ",
            "    .**...**.   "
        },

       };
}

 

 

 

 

하이 라키 뷰에 GameManager 오브젝트를 만들고 GameManager 스크립트를 생성하여 오브젝트에 붙입니다

 

 

 

GameManager 스크립트 작성

 

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

public class GameManager : MonoBehaviour
{
    //클릭한 카드 번호
    static public int cardNum;

    //직전의 카드 번호
    int lastNum = 0;

    //스테이지의 전체 카드수
    int cardCnt;

    //카드 클릭 횟수
    int hitCnt = 0;

    //스테이지 번호
    static public int stageNum = 1;

    //스테이지 수
    int stageCnt = 6;

    //카드 배열 카드 섞기용
    int[] arCards = new int[50];

    //게임 시작 시간
    float startTime;

    // 스테이지 경과 시간
    float stageTime;


    // Start is called before the first frame update
    void Start()
    {
        //시간초기화
        startTime = stageTime = Time.time;
        //스테이지 만들기
         StartCoroutine(MakeStage());
    }

    void Update()
    {

    }

    IEnumerator MakeStage()
    {
        //시작카드의 x좌표
        float sx = 0;

        //시작카드의 z좌표
        float sz = 0;

        
        SetCardPos(out sx, out sz);

        //시작카드의 번호
        int n = 1;

        //카드배열 읽기 배열의 1행을 읽고 변수 t에 할당한다
        string[] str = StageSet.stage[stageNum - 1];

        //배열의 행의 수만큼 반복
        foreach (string t in str)
        {
            // 각행의 문자열을 단일 문자 배열로 변환(문자열 좌우의 공백 제거) , 변수t의 자우 공백을 제거(Trim)하고 단일 문자배열로 변환
            char[] ch = t.Trim().ToCharArray();

            //카드의 x축 좌표
            float x = sx;

            //1행의 문자열 길이만큼 반복
            //배열의 ch의 한문자를 읽고 변수 c에 할당한다
            foreach (char c in ch)
            {
                switch (c)
                {

                    //맵의 내용이 * 이면 그위치에 카드 만들어서 배치
                    case '*':
                        //카드 만들기
                        GameObject card = Instantiate(Resources.Load("Prefab/Card")) as GameObject;

                        //카드 좌표설정
                        card.transform.position = new Vector3(x, 0, sz);

                        //태그 달기
                        card.tag = "card" + n++;
                       // card.tag = "card" + arCards[n++];
                        x++;
                        break;

                        //빈칸 처리
                    case '.':
                        x++;
                        break;


                        //반 칸 공백처리
                    case '>':
                        x += 0.5f;
                        break;


                        //반 줄 행간 처리
                    case '^':
                        sz += 0.5f;
                        break;


                }

                //카드를 표시한 후에는 지연 시간을 두어 카드가 배치되는 과정이 보이도록함
                if (c == '*')
                {
                    yield return new WaitForSeconds(0.03f);

                }
            }

            //한줄 아래로 이동
            sz--;
        }

    }

    //카드의 시작 위치 계산
    void SetCardPos(out float sx, out float sz)
    {
        //가로 카드수 반 칸 공백 포함
        float x = 0;

        //세로 행수 반줄 행간 포함
        float z = 0;

        //가로 카드 최대수
        float maxX = 0;

        //스테이지 전체 카드수
        cardCnt = 0;

        //카드 배열 조사 맵 배열 을 읽음
        string[] str = StageSet.stage[stageNum - 1];

        //행의 수만큼 반복
        for (int i = 0; i < str.Length; i++)
        {
            //1행읽기
            string t = str[i].Trim();

            //각행의 카드수
            x = 0;

            //각행의 글자 수만큼 반복
            for (int j = 0; j < t.Length; j++)
            {
                //문자열(string)은 단일 문자(char)의 배열로 취급할수 있음
                switch (t[j])
                {
                    case '.':
                    case '*':

                        //카드배치에 필요한 공간
                        x++;
                        if (t[j] == '*')
                        {
                            //전체 카드수
                            cardCnt++;
                        }
                        break;
                    case '>':
                        x += 0.5f;
                        break;
                    case '^':
                        z -= 0.5f;
                        break;

                }
            }

            //각 행의 최대 카드수 계산
            if (x > maxX)
            {
                maxX = x;
            }
            //전체 행의 수
            z++;
        }
        //카드 가로 시작 위치
        sx = -maxX / 2;
        sz = (z - 1) / 2;

        //  StartCoroutine(CardOpen(cardCnt));
    }

}

 

 

 

 

하이 라키 뷰에 MaskCamera를 잠시 꺼두고 아래 그림과 같이 Transform 값을 바꿉니다

 

Background1 Transform값을 바꿉니다

 

Background2 Transform값을 바꿉니다

 

 

 

Main Camera Transform 값을 바꿉니다

 

게임을 실행시키면 아래와 같이 게임 화면에 카드가 배열되는 모습을 볼 수 있습니다

오른쪽 약간의 공백은 텍스쳐가 들어갈 공간을 남겨두어서 약간 왼쪽으로 치우친 모습입니다

 

반응형
반응형

 

그림을 나타낼 큐브를 만들고 애니메이션을 주는 작업을 하겠습니다

먼저 GameObject에서  Cube을 생성합니다

 

 

cardGame_texture.unitypackage
3.18MB

Resources 폴더에 그림파일을 임포트 합니다

 

 

 

 

Materials 폴더에 Material을 생성하고 이름을 card_back으로 하고 Card 오브젝트 에 메터리얼을 드래그하여 붙이고 메터리얼 Shader를 Lagacy Shaders -> Transparent -> Diffuse를 선택합니다

그리고 그림을  Resources 파일에 있는 back 5를 붙입니다

 

Card 오브젝트에 붙일 CardCtrl 스크립트를 생성하여 스크립트 작업을 합니다

 

CardCtrl 스크립트 작성

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

public class CardCtrl : MonoBehaviour
{
    //이미지 번호
    int imgNum = 1;

    //카드 뒷면 이미지 번호
    int backNum = 1;

    // 오픈된 카드의 판별여부 
    bool isOpen = false;

    Animator anim;


    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();
;
    }


    //카드 앞면을 이미지을 가지고 온다
    void ShowImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("card" + imgNum) as Texture2D;
    }


    //카드 뒷면 이미지을 가지고 온다
    void HideImage()
    {
        transform.GetComponent<Renderer>().material.mainTexture = Resources.Load("back" + backNum) as Texture2D;
    }
}

 

 

 

 

작성한 스크립트를 Card 오브젝트에 붙입니다

 

메인 카메라를 선택하고 Transform을 그림과 같이 바꿉니다

 

 

 

 

BackManager 자식으로 Camera 오브젝트를 생성하여 이름을 MaskCamera 라하고  Transform을 아래 그림과 같이 바꿉니다

 

 

Card 오브젝트를 선택하고  Window -> Animation -> Animation을 선택합니다

 

aniOpen  애니메이션을 생성하고 붉은 단추를 누르고 0 프레임에  Rotation Z값을 0을 줍니다

 

aniOpen  애니메이션을  30 프레임에  Rotation Z값을 -180을 줍니다

 

 

 

aniOpen 애니메이션에 15 프레임 정도에 Add event를 넣고 ShowImage() 함수를 연결합니다

 

aniOpen 애니메이션에 0 프레임에 Add event를 넣고 HideImage() 함수를 연결합니다

 

 

그리고  aniClose 애니메이션을 생성하고 빨간 단추를 누르고 0 프레임에 Rotation Z값을 -180 을 주고 30프레임에 Rotation Z값을 0을 줍니다

 

 

 

aniClose 애니메이션을 선택하고 0프레임에 Add event를 추가하고 ShowImage() 함수를 연결합니다

 

aniClose 애니메이션을 선택하고 15 프레임에 Add event를 추가하고 HideImage() 함수를 연결합니다

 

aniClose, aniOpen 애니메이션의  Loop Time을 체크 해제합니다

 

 

 

Animator에 들어가서 Create State -> empty를 생성하여  Set as layer Default state를 선택합니다

 

 

Resources 폴더에 Prefab 폴더를 생성하고 Card 오브젝트를 드래그하여 프리 팹을 만듭니다

 

 

하이라키에 있는 Card 오브젝트는 삭제합니다

반응형
반응형

카드 그림을 맞추는 게임을 만들어 보겠습니다

 

 

 

이게임은 고도의 집중력과  기억력이 요구되는 게임입니다

먼저 인터넷에서 게임에 쓰일 배경 이미지를 모읍니다

무료 배경 이미지를 인터넷에 들어가면 많이 찾을 수 있을 것입니다

 

 

 

먼저 Project 뷰에서 Resources 폴더를 만듭니다

 

 

인터넷에서 다운 배경 이미지 파일을 이름을 imgBack0~5까지 순번 되로 이름을 적습니다

이름 스팰링 하나 틀리면 안 되니 주의하여야 합니다

 

빈 오프젝트를 생성하고 이름을 BackManager 라 하고 메인 카메라와 라이트를 자식으로 둡니다 

 

 

 

그리고 GameObject -> 3D Object -> Plane을 하나 생성합니다

 

Plan의 이름을 Background1으로 하고 backManager 자식으로 둡니다

 

project 뷰에서 Materials 폴더를 만들고 Material을 생성합니다

 

 

 

Material의 이름을 back 1으로 합니다 

 

 

Background1을 선택하고 스케일을  2.3 ,1, 1.2로 하고 back 1 메터리얼을 드래그하여 붙입니다

그리고 Shader 속성을  Lefacy Shaders -> Transparent -> Diffuse를 선택하고 Resources 폴더에 있는  배경 이미지를  갖다 붙입니다 

 

 

 

 

Background1 오브젝트를 선택하여 Ctrl_D 키를 누르고 복사된 오브젝트를 이름을 Background2 라하고 Position  y 값을  -0.5를 줍니다

 

 

 

Project 뷰에서 Scrips 폴더를 만들고 BackManager 스크립트를 생성합니다

 

BackManager 스크립트 작성

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

public class BackManager : MonoBehaviour
{
    //배경 이미지
    Transform back1;
    Transform back2;

    //오버랩 지연 시간
    int delayTime = 3;

    //현재의 시간
    float currentTime = 0;


    //이미지 갯수
    int imgCnt = 6;
    //현재 이미지 번호
    int imgNum = 1;

    void Start()
    {
        //배경찾기
        back1 = transform.Find("Background1");
        back2 = transform.Find("Background2");


    }
    // Update is called once per frame
    void Update()
    {
        //지연시간 처리
        currentTime += Time.deltaTime;

        if (currentTime >= delayTime)
        {
            currentTime = 0;

            //이미지 오버랩
            StartCoroutine(OverlapImage());
        }

    }

    IEnumerator OverlapImage()
    {
        //이미지 알파값 설정
        //이미지의 두명도는 0~1의 값이다 0은 완전 투명 , 1은 완전불투명
        for (float alpha = 1; alpha >= 0; alpha -= 0.05f)
        {
            //배경이미지를 점점 투명하게 만든다
            back1.GetComponent<Renderer>().material.color = new Vector4(1, 1, 1, alpha);

            //배경이미지를 점점 불투명하게 만든다
            back2.GetComponent<Renderer>().material.color = new Vector4(1, 1, 1, 1 - alpha);


            //화면 갱신까지 대기
            yield return new WaitForFixedUpdate();

        }

        //배경 바꾸기
        Transform tmp = back1;
        back1 = back2;
        back2 = tmp;

        //다음 이미지 준비
        //Mathf.Repeat() 0~<한계값-1> 을 반복으로 처리하는 함수
        imgNum = (int)Mathf.Repeat(++imgNum, imgCnt);

        //배경2에 새로운 이미지 할당
        back2.GetComponent<Renderer>().material.mainTexture = Resources.Load("imgBack" + imgNum) as Texture2D;

        currentTime = 0;
    }
}

 

 

 

BackManager 스크립트를  BackManager 오브젝트에 붙입니다

 

게임을 실행하면 아래 동영상과 같이 Resoceses 폴더에 들어 있는 이미지 imgBack0 ~1  순번대로 변환되는 것을 보실 수 있습니다 

 백그라운드 그림 이미지를 자연스럽게 바꾸는 방법을 살펴보았습니다 

 

반응형
반응형

마징가 제트는  가장 큰 특징은 뭐니 뭐니 해도 인간이 로봇에 탑승한다는 개념이다. 이는 70년대 일본에서 자동차가 상당히 고급 물품이었던 데다 자동차를 조종하는 기술이 특수한 기술로 여겨져 아무나 할 수 없는 동경의 대상으로 여겨진 시대상을 반영한다.

그리고 마징가 Z는 여기서 더 나아가 인간과 로봇이 일체화된다는 구성을 선보인다. 작중의 묘사를 보면 카부토 코우지는 단순히 마징가 Z에 올라타는 것만이 아니다. 마징가 Z의 위기가 곧 카부토 코우지의 위기이고, 코우지의 사망이 곧 마징가 Z의 불능화로 인식되고 있다는 점을 알 수 있다. 또 마징가 Z의 캐릭터성이 오직 코우지가 탑승한 것을 전제로 형성되고 있는 것도 함께 눈여겨 봐야 할 부분이다. 동서양을 통틀어 모든 SF에서는 로봇이란 독립된 캐릭터를 부여받기 때문에 마징가 Z의 묘사는 낯선 것이다.

코우지가 마징가 Z에 타는 것은 단순한 조종자가 되는 정도를 넘어서 마징가 Z라는 로봇의 "자아"가 된다는 뜻이고, 마징가 Z는 코우지의 새로운 육체가 된다는 것이다. 따라서 이것은 본질적으로 파일럿으로서 탑승하는 일이나 사이보그화 되는 것(인간이 기계가 되는 것)과는 근본적인 차이를 가진다. 마징가 Z와 코우지는 생물과 기계를 초월하여 양분되어서는 성립할 수 없는 동일시된 존재이라는 말이며, 이 점은 서구 SF계에서는 보기 힘든 철저히 동양적인 사고 관인 것이다. 이 때문에 서양에선 마징가 Z를 비롯한 일본의 거대 로봇 애니메이션을 일본에만 있는 것이라 하며 재패니즈 로봇, 쇼군 로봇이라고 따로 분류했을 정도였다.


작품 내에서도 이런 속성은 "부려지는 기계"인 기계수와 대비되며 더욱 뚜렷해진다. 일부 기계수는 나름대로 자아를 가지고 창조주인 닥터 헬에게 반항하기도 하지만, 그들은 어디까지나 "기계"이고 AI이다. 부여된 속성에 안주하고 거기에 충실할 뿐인 로봇이다. 인간이자 로봇인 마징가 Z는 그들이 지닌 기기묘묘한 능력에 대처하기 위해 스스로 성장하며 보다 완전한 존재로 거듭나게 되지만, 기계수들은 결국 성장하지 못한다. 이런 차이를 극적으로 표현한 에피소드가 제트 스크랜더의 등장이다.

마징가 Z의 수많은 강화극 중에서도 특히 제트 스크랜더가 고평가되는 것은 마징가 Z가 가진 근본적인 한계를 끝없는 향상심을 통해 극복하게 되는 것 때문인데, 여기서 기계수는 "하늘을 날 수 있는 이점" 때문에 한때 마징가 Z를 궁지로 몰아넣지만 단지 그렇게 부여된 속성 이상 아무것도 가지지 못하고 그 이상을 가지도록 노력할 수도 없는 존재이기 때문에 끊임없이 향상되는 마징가 Z 앞에서 결국 마지막에 무릎을 꿇을 수밖에 없다. 이런 개조극은 기계로서의 속성과 성장을 통한 인간 된 속성을 동시에 표현했던, 매우 인상적인 연출로 오늘날까지도 기계와 인간의 차이를 융합시킨 뛰어난 해답이라 평가할 수 있겠다.

나가이 고는 탑승형이란 개념을 착안한 것에 대해서 철인 28호 같은 조종형은 조종자가 안전한 곳에서 조종만 하는 것은 지도자가 젊은이 병사들을 전장으로 보내서 조종하는 현대전과 비슷한 맥락의 비겁한 느낌이기 때문에 배제하였고, 철완 아톰 같은 인공지능 로봇은 아무리 인공지능 기술이 발전해도 로봇은 인간보다 강한 육체를 가지고 있어 선민의식을 가지게 될 것이고, 이것은 곧 인간성의 결여로 이어진다고 생각해서 부정적으로 보았다. 나가이 고는 인간이 직접 탑승해서 목숨을 걸고 전장에 나가는 로봇을 만들어 인간성과 사람을 죽인다는 행위의 책임감을 강조하였고 "신도 악마도 될 수 있다" 라는 대사는 곧 싸움에는 책임을 지라는 의미라고 한다

 

 

마징가 제트를 3DS 모델링 하였습니다 

 정면 모습 

 

 

오른쪽 측면

 

 

 

뒷모습

 

 

왼쪽 측면

 

메탈 느낌의 렌더링을 좀더 공부를 해야겠습니다 ㅎㅎ

좀 더 멋진 모습으로 ㅎㅎ

 

 

걷기 동영상

 

 

 

마징가 제트 처럼 신나는 액션게임 

다운로드

https://play.google.com/store/apps/details?id=com.Magatron.MonsterSky

반응형
반응형

엽총의 표준 게이지는 2와 3/4인치 약실에 12, 16, 20 구경을 말한다.
1960년경만 해도 이 구경의 약실은 2.5인치였다. 이러한 구형 총들은 아직도 많이 사용되고 있다.
그러므로 구형엽총을 사용하는 사람들은 엽탄 선택에 세심한 주의를 기울여야 한다.
12 구경은 지름이 18.4mm인 엽총으로 세계적으로 가장 널리 사용되고 있는 총이다.
16 구경은 내부 지름이 17mm인 총으로 일부 층이 사용하고 있다.
20 구경은 16mm로 정교하고 세밀하여, 반동이 작은 총을 선호하는 사람이 애호한다.
12 구경은 유럽의 경우 32g 엽탄을 주로 사용하고 있으며 16 구경의 경우 28g, 20 구경의 경우 24g을 사용한다.
총의 무게도 구경이 작아 질수록 줄어든다. 즉, 12 구경은 2.9∼3.9kg, 16 구경은 2.6∼2.9kg, 20 구경은 2.4∼2.7kg이다.
따라서 구경이 작아질수록 총기를 다루기는 편하지만 성능은 떨어진다.
그러나 최근에는 기관부나 탄환장전장치를 알루미늄 합금으로 제조하는 기술이 개발되어 12 구경의 무게를 2.7kg 또는 그 이하로 감소시킨 총이 생산되고 있어 총의 무게에 대한 개념이 달라지고 있다.
16 구경이나 20 구경은 큰 멧돼지 사냥에는 효력이 떨어지기 때문에 바람직하지 않다. 보통 26∼28g의 산탄을 내뿜는 20 구경은 숲 속에서의 사냥과 근거리 사냥에 훌륭한 성능을 발휘한다.
물론 오리 사냥이나 기타 큰 게임의 사냥에도 적절하지 않다.
따라서 유럽지역에서는 12구경을 다기능 구경으로 인정받게 될 것으로 보이며 앞으로도 12 구경은 최고의 신뢰를 얻게 될 것으로 보인다.
12 구경 총은 24∼42g의 산탄을 뿜어낼 수 있는 총으로 거의 모든 상황에서 사냥하기에 적당한 것으로 보고 있다.
다만 화약이나 가스압력을 고려해 볼 때 30∼36g의 엽탄을 사용하는 것이 효과적이다.
탄환의 속도, 패턴, 명중도 등의 효력이 가장 높기 때문이다.
20 구경은 탐미적인 엽사들을 위해 그 특수성을 강화시켜 나가야 될 것으로 판단된다.
20 구경으로 가장 좋은 결과를 얻으려면 24∼28g의 엽탄을 사용해야 한다.

 

3D 맥스로 오브젝트 만들기

 

총열 개머리판 본체를 나누어  맵 피기 

 

 

 

포토샵에서 맵그림을 그리고 스탠더드 메터리얼에 맵을 씌웁니다

 

동영상을 올려봅니다 ㅎㅎ

독일 멜켈12구경 엽총

 

반응형
반응형

전 세계가 초토화되고 좀비들의 습격이 시작됩니다
미친 좀비들이 사정없이 공격하여 옵니다 
쳐들어 오는 좀비를 철조망이 무너지지 않게 좀비들을 무찌릅니다
좀비들을 없애면 랜덤으로 골드가 생성됩니다
생성된 골드를 획득하고 메인 요새 화면으로 돌아가 캐릭터를 강력하게 할 수 있는 무기를 구매합니다
총알도 강력하게 업그래드 할 총알을 구매할 수 있습니다
무기를 구매하여 포켓 지갑에 넣고 인간 요새를 지키기 위해 철조망을 사이에 두고 좀비들을 무찌릅니다
스테이지가 올라갈수록 좀비들의 숫자가 늘어나고 좀비들의 체력이 올라 감니다 
그리고 강력한 좀비 보스들이 출현 합니다 
화끈하게 좀비들을 쳐부술 수 있는 무기를 구해하여 좀비들을 없애고 스테이지를 올려 점점 강력한 좀비들과 대결을 하세요 
지구의 운명이 당신에게 있습니다 

구글 플레이 다운로드

 

마지막 남은 희망의 장소 인간의 기지를 지키는 주인공을 공격해 오는 좀비 떼들

 

철조망에 걸려든 좀비들 인간들에게 득달같이 달려듭니다

 

무시무시한 좀비들이 밤낮 가릴 것 없이 공격해오고 인간들은 강력한 보스 좀비의 공격을 막아 내야 합니다

 

게임 속에 캐릭터는 다양한 공격 무기를 탑재할 수 있습니다

좀비 떼의 공격을 막아내고 좀비들을 죽이면 금화가 생성됩니다

금화를 모아 캐릭터를 강화시킬 수 있는 강력한 무기와 탄약을 구입하세요

강력한 무기를 구입하면 더욱 게임을 즐겁게 즐길 수 있게 캐릭터를 강화시킵니다

 

화끈한 좀비 박멸로 좀비들을 철조망이 뚫리지 않도록 철조망을 보호하고 좀비들을 무찌르세요 

지구의 운명이 당신에게 있습니다 

 

구글 플레이 다운로드 

반응형
반응형

하이 라키 뷰에서 Gun 오브젝트를 선택하여 AddComponent -> Animation을 생성합니다 

 

 

하이 라키 뷰에 Gun 오브젝트를 선택하고  Window -> Animaton -> Animaton을 클릭합니다 

 

 

Animation 타임라인에서 Create 를 누르고  Assets에 Animation 폴더를 생성하고  파일 이름을 ChargeBullet으로 하여  저장합니다 

 

 

 

아래 동영상과 같이 타임 라인에 왼쪽 위에 빨간 버튼을 누르고 총이 왼쪽으로 회전 하는 애니메이션을 만듭니다

각각 타임라인에  라인 바를 갖다 놓고 총의 움직임을 주면선 애니메이션을 만듭니다

 

하이 라키 뷰에 Gun 오브젝트를 선택하고 자식으로 Cyilnder를 생성하고 position 0, 0.04, 0.251

Rotation 90, 0, 0   Scale 0.1, 0.15, 0.1로 맞춥니다 

총을 쏘았을 때 탄피가 튕겨 나오는 애니메이션을 할 수 있게 총안쪽에  보이지 않는 위치에 놓습니다 

 

 

 

Gun오브젝트의 Cylinder를 선택하고 window -> Animation -> Animation을 선택하여 빨간 버튼 아래에 있는 애니메이션 선택 바를 누르고 Create new Clip을 선택하여 애니메이션 이름을 Fire 하고 저장합니다 

 

 

 

 

 

그림과 같이 탄피가 나오는 애니메이션을 Cylinder를 타임라인에 움직이며 애니메이션을 줍니다 

 

Gun 오브젝트를 선택하고 Animation에 그림과 같이 애니메이션을 연결합니다 

Gun 오브젝트를 선택하고 자식으로 빈 오브젝트를 만들어 그림과 같이 총끝머리에 위치시키고 이름을 spPoint라고 합니다 차후에 총알 발사 위치와 총알 화염 이팩트가 터지는 위치입니다 

 

 

Gun 오브젝트를 선택하고 Gun Tag를 만들어서 Tag를 Gun으로 합니다  

 

 

 

source_effect.unitypackage
0.01MB

 

이팩트 소스를 다운로드하여 그림과 같이 Prefabs 폴더에 이팩트 파일을 저장합니다 

 

 

 

 

 

GunCtrl 스크립트 수정

 

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

public class GunCtrl : MonoBehaviour
{
    //총 발사음
    public AudioClip sndFire;
    //실탄 장전음
    public AudioClip sndCocking;
    //배경음악
    public AudioClip sndStage;
    //게임 오버음
    public AudioClip sndOver;

    //프리팹 접시,참새, 폭파화염,총구화염, 총알
    public Transform clay, bird, exp, gunFire, fireBullet;

    //탄피,총구위치
    Transform bulletCase, spPoint;

    //실패 횟수
    static public int miss;

    //성공횟수
    int hit;

    //남은 총알
    int bulletCnt;

    //시작시간, 종료시간
    float startTime, overTime;
    bool gameOver = false;

    //화면폭과높이
    int width, height;

    void Start()
    {
        Screen.orientation = ScreenOrientation.Landscape;
        Screen.sleepTimeout = SleepTimeout.NeverSleep;

        Cursor.visible = false;

        SetStage();
    }
    private void Update()
    {
        // 10번 미스 되면 게임 오버
        if (miss >= 10)
        {
            gameOver = true;
            overTime = Time.time;
        }

        if (gameOver)
        {
            return;
        }

        // Esc 누르면 게임종료
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            EditorApplication.isPlaying = false;
        }

        MakeClay();
        RotateGun();

        if (Input.GetMouseButtonDown(0))
        {
            FireGun();
        }
        if (bulletCnt < 5 && Input.GetMouseButtonDown(1))
        {
            StartCoroutine(ChargeGun());
        }

    }

    //총회전 
    void RotateGun()
    {
        //카메라부터 거리를 설정
        Vector3 pos = Input.mousePosition;

        //화면의 기준으로 총의 움직임을 제한한다
        pos.x = Mathf.Clamp(pos.x, 0, Screen.width);
        pos.y = Mathf.Clamp(pos.y, 0, Screen.height);

        //카메라로 부터의 거리
        pos.z = 13.2f;
        //마우스 위치를 월드 좌표로 변환
        Vector3 view = Camera.main.ScreenToWorldPoint(pos);

        //총의 회전
        transform.LookAt(view);

    }

    void FireGun()
    {
        //실탄 장전 애니메이션이 진행중이면 발사금지
        if (GetComponent<Animation>().isPlaying)
        {
            return;
        }

        RaycastHit hit;

        //카메라 시점에서 커서 위치 계산
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        Physics.Raycast(ray, out hit, Mathf.Infinity);

        //총을 클릭하면 실탄 재장전
        if (hit.transform.tag == "Gun" && bulletCnt < 5)
        {
            StartCoroutine(ChargeGun());
            return;
        }

        //총구 앞에 화면
        Instantiate(gunFire, spPoint.position, Quaternion.identity);
        //총탄 발사 
        Instantiate(fireBullet, spPoint.position, spPoint.rotation);
        //탄피 배출 애니메이션
        GetComponent<Animation>().Play("Fire");

        CheckTarget(hit);

        //남은 실탄 수 처리
        bulletCnt--;
        if (bulletCnt<= 0)
        {
            StartCoroutine(ChargeGun());
        }
    }

    //목표물 적중여부 판정
    void CheckTarget(RaycastHit hit)
    {
        switch (hit.transform.tag)
        {
            //접시 명중되었을 때 점수 처리및 폭발이팩트 생성및 제거
            case "Clay":
                this.hit++;
                Instantiate(exp, hit.transform.position, Quaternion.identity);
                Destroy(hit.transform.gameObject);
                break;

                // 참새 사망시 미스 처리및 참새연결 스크립트의 DeadBird 함수 와 통신 
            case "Bird":
                Instantiate(gunFire, hit.transform.position, Quaternion.identity);
                miss++;
                hit.transform.SendMessage("DeadBird", SendMessageOptions.DontRequireReceiver);
                break;

        }
    }

    //장전 애니메이션 
    IEnumerator ChargeGun()
    {
        while (GetComponent<Animation>().isPlaying)
        {
            yield return 0;
        }

        GetComponent<Animation>().Play("ChargeBullet");
        yield return new WaitForSeconds(0.5f);
        bulletCnt = 5;
    }

    //접시 생성
    void MakeClay()
    {
        if (Random.Range(0, 1000) > 970 && !GetComponent<Animation>().isPlaying)
        {
            if (Random.Range(0,100)< 70)
            {
                Instantiate(clay);
            }
            else
            {
                Instantiate(bird);
            }
        }
    }

    //장전 음악 
    void SoundClick()
    {
        bulletCase.gameObject.GetComponent<Renderer>().enabled = true;
        AudioSource.PlayClipAtPoint(sndCocking, Vector3.zero);
    }

    //탄발사음 
    void SoundFire()
    {
        bulletCase.gameObject.GetComponent<Renderer>().enabled = true;
        AudioSource.PlayClipAtPoint(sndFire, Vector3.zero);
    }

    //탄피 감추기 애니메이션
    void HideBullet()
    {
        bulletCase.gameObject.GetComponent<Renderer>().enabled = false;
    }
    void SetStage()
    {
        width = Screen.width;
        height = Screen.height;

        spPoint = GameObject.Find("spPoint").transform;

        bulletCase = transform.Find("Cylinder");

        bulletCase.gameObject.GetComponent<Renderer>().enabled = false;

        startTime = Time.time;
        hit = miss = 0;
        bulletCnt = 5;
        gameOver = false;

        GetComponent<AudioSource>().loop = true;
        GetComponent<AudioSource>().Play();
    }

    void OnGUI()
    {
        // GUI.skin = skin;

        //게임진행시간 
        float time = Time.time - startTime;

        if (!gameOver)
        {
            //커서위치에 조준점 표시
            GUI.DrawTexture(new Rect(Input.mousePosition.x - 24, height - Input.mousePosition.y - 24, 48, 48),
                            Resources.Load("crossHair") as Texture2D);
        }
        else
        {
            time = overTime - startTime;
        }

        //남은 실탄수 표시
        for (int i = 1; i <= bulletCnt; i++)
        {
            GUI.DrawTexture(new Rect(i * 12, height - 20, 8, 16), Resources.Load("bullet") as Texture2D);
        }

        //점수 등 표시
        string sHit = "<size='30'>HIT : " + hit + "</size>";
        string sMiss = "<size='30'>MISS : " + miss + "</size>";
        string sTime = "<color='yellow'><size='30'>Time : " + (int)time + "</size></color>";

        GUI.Label(new Rect(30, 20, 120, 40), sHit);
        GUI.Label(new Rect(width / 2 - 40, 20, 160, 40), sTime);
        GUI.Label(new Rect(width - 120, 20, 120, 40), sMiss);

        string msg = "Shoot : Left Btn  Charge : Gun Click";
        GUI.Label(new Rect(width - 380, height - 40, 380, 40), msg);

        //게임오버 처리
        if (gameOver)
        {
            Cursor.visible = true;
            if (GetComponent<AudioSource>().clip != sndOver)
            {
                GetComponent<AudioSource>().clip = sndOver;
                GetComponent<AudioSource>().loop = false;
                GetComponent<AudioSource>().Play();
            }

            //Play Game 버튼 생성 및 버튼On 시 게임다시 진행
            if (GUI.Button(new Rect(width / 2 - 70, height / 2 - 50, 140, 60), "Play Game"))
            {
                Application.LoadLevel("MainGame");
            }

            //게임에서 나옴
            if (GUI.Button(new Rect(width / 2 - 70, height / 2 + 50, 140, 60), "Quit Game"))
            {
                EditorApplication.isPlaying = false;
            }
        }
    }
}

 

 

 

 

 

 

 

 

GunCtrl 스크립트를 작성하고   Sound 파일에 있는 음악 소스 파일을 그림과 같이 연결합니다 

 

Prefabs파일에 있는 프리 팹을 GunCtrl 스크립트에 연결합니다 

 

 

Gun 오브젝트를 선택하고  AudioSource를 만들어서 AudioClip에 Stage 파일을 연결합니다

 

 

 

 

 

 

 

Gun오브젝트를 선택하고 애니메이션 타임라인을 열고 애니메이션 타임라인 3/2 되는 지점에 애니메이션 이벤트를 만들어서 Function을 SoundClick() 함수에 연결합니다 

 

Fire 애니메이션을 선택하고 첫 타임라인에 SoundFire() 함수를 연결하는 애니메이션 이벤트를 만듭니다 

 

Fire 애니메이션 마지막 타임 라인에 HideBullet() 함수를 연결하는 애니메이션 이벤트를 만듬니다 

 

ClayCtrl 스크립트를 열고 아래 그림과 같이 GunCtrl.miss++; 를 추가합니다 

 

 

 

 

 

 

 

 

아래 동영상과 같이 총쏘는 게임을 만들어 보았습니다 

여기까지 따라 오신분들 너무 수고 많으셨습니다

감사해요 ㅎㅎ

 

반응형
반응형

하이 라키 뷰에 오른쪽 마우스를 누르고 3D object -> Cylinder를 만듭니다

 

그리고 스케일을 조정하여 1, 0.02, 1 을 값을 넣습니다 

 

그림과 같이 납작한 접시 모양이 됩니다 

 

하이라키 뷰에 Cylinder의 CrayCtrl 스크립트를 만들어 붙입니다 

ClayCtrl 스크립트작성

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

public class ClayCtrl : MonoBehaviour
{

    float speed; //접시 속도
    float rotZ; //접시 각도
    float gravity; // 추락속도

    int[] dir = { -1, 1 };
    int dirX; // 이동방향
    void Start()
    {
        SetPosition();
    }

    // Update is called once per frame
    void Update()
    {
        //접시의 이동속도를 감속한다
        gravity -= 1.3f * Time.deltaTime;
        //접시의 이동방향과 속도를 설정

        //접시를 회전을 주어 월드좌표로 이동
        Vector3 move = new Vector3(speed * dirX, gravity, 0) * Time.deltaTime;
        transform.Translate(move, Space.World);


        //화면에 접시가 벗어 나면 제거 한다
        if (Mathf.Abs(transform.position.x)>8 || transform.position.y < -3)
        {
           // GunCtrl.miss++;
            Destroy(gameObject);
        }
        
    }

    void SetPosition()
    {
     // 랜덤으로 접시속도 설정
        speed = Random.Range(3, 5f);

        //추락속도 설정
        gravity = 2f;

        //이동 방향 설정
        dirX = dir[Random.Range(0, 2)];

        //화면 의 좌우 위치
        float posX = -8 * dirX;

        //접시의 높이 및 회전설정
        float posY = Random.Range(2.5f, 4);
        transform.position = new Vector3(posX, posY, 9);
        transform.eulerAngles = new Vector3(-50, 0, Random.Range(10, 20f) * dirX);
    }
}

 

 

에셋에 Prefabs 폴더를 만들고 Clay 오브젝트를 드래그하여 프리 팹을 만들고 하이 라키 뷰에 있는 Clay는 삭제합니다 

 

 

참새가 죽을 때 나오는 꽥 오브젝트를 만들겠습니다.

Texture 폴더에 있는 꽥 그림파일을 하이 라키 뷰에 드래그하여 올려놓습니다 

 

OCtrl 스크립트를 만들고 꽥 오브젝트에 붙입니다 

 

OCtrl 스크립트 작성

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

public class OCtrl : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Destroy(gameObject, 1.5f);
    }


}

 

 

 

하이 라키뷰에 있는 꽥 오브젝트를 Prefabs 폴더에 드래그 하여 프리팹을 만듬니다 

하이라키뷰에 꽥 오브젝트는 삭제합니다 

 

 

 

하이라키 뷰에 Cube 오브젝트를 생성하여 스케일을 1, 0.02, 1을 맞춥니다 

 

Cube의 이름을 FireBullet으로 바꾸고 FireBullet 스크립트를 생성하여 붙입니다 

 

 

FireBullet 스크립트 작성

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

public class FireBulletCtrl : MonoBehaviour
{

    //속도
    float speed = 60f;

    // 총알 삭제 시간
    float delay = 0.5f;
    void Update()
    {
        // 총알 속도 설정
        float Move = speed * Time.deltaTime;

        //총알의 이동 방향 설정
        transform.Translate(Vector3.forward * Move);

        // 총알 삭제를 설정한다 
        delay -= Time.deltaTime;
        if (delay <= 0)
        {
            Destroy(gameObject);
        }
    }
}

 

 하이 라키 뷰에 FireBullet 오브젝트를 드래그 하여 Prefabs 폴더에 생성합니다 

그리고 하이 라키뷰에 있는 오브젝트는 삭제 합니다  

 

하이라키 뷰에  Quad를 생성하여 스케일을  0.8, 0.8 ,1로 맞춥니다 

 

 

 

Materials 폴더에 메터리얼을 생성하고 이름을 Bird 라하고 Shader -> Unlit/Transfarent를 하고 Sparrow그림파일을 선택하여 나타나게 합니다 

 

Materials파일에 있는 Bird 메터리얼을 Bird 오브젝트에 드래그하여 붙입니다 

 

Scripts폴더에  BirdCtrl 스크립트를 만들고 BirdCtrl 스크립트를 작성합니다 그리고 Bird 오브젝트에 붙임니다

 

 

BirdCtrl  스크립트 작성

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

public class BirdCtrl : MonoBehaviour
{
    //사망시 프리팹 연결 오브젝트
    public Transform oCtrl;


    //참새 이동 속도
    float speed;

    //죽었는지? 아닌지?
    bool isDead = false;

    void Start()
    {
        SetPosition();
        
    }


    void Update()
    {
        //속도 설정
        float Move = speed * Time.smoothDeltaTime;

        
        if (!isDead)
        {
            //날아갈때 오른쪽으로 이동
            transform.Translate(Vector3.right * Move, Space.World);
        }
        else
        {
            //떨어질대 아래로 이동
            transform.Translate(Vector3.down * Move, Space.World);
        }

        //화면을 벗어나면 제거
        if (transform.position.x > 7 || transform.position.y < -3)
        {
            Destroy(gameObject);
        }
    }

    //참새 사망시
    void DeadBird()
    {
        isDead = true;

        //참새사망시 180도 회전 설정
        transform.eulerAngles = new Vector3(0, 0, 180);

        //참새 위치 계산를 계산하여 꽥 오브젝트 생성할 위치을 설정하고 참새가 죽으면 생성한다
        Vector3 pos = new Vector3(transform.position.x, transform.position.y + 0.2f, 8);
        Quaternion rot = Quaternion.identity;

        rot.eulerAngles = new Vector3(0, 0, 0);
        Instantiate(oCtrl, pos, rot);
    }

    void SetPosition()
    {
        //참새의 속도 설정
        speed = Random.Range(2, 3f);

        //참새의  랜덤 위치설정
        transform.position = new Vector3(-7, Random.Range(1.4f, 4.7f), 8);
    }
}

 

 

Bird 오브젝트를 Prefabs폴더에 드래그하여 프리 팹을 만들고 Bird 프리팹을 선택하여 O Ctrl  자리에  꽥 프리팹을 연결합니다 

그리고 하이 라키 뷰에 있는  Bird오브젝트는 삭제합니다 

 

Biird 오브젝트를 선택하고 Tag에 Bird를 생성하여 태그 합니다 

 

Clay 프리 팹을 선택하여 Tag Clay를 선택하여 태그 합니다 

반응형
반응형

하이 라키 뷰에 빈 오브젝트를 만들고 이름을 Gun으로 하고 Transform position과 Rotation을 0으로 맞춥니다

 

 

FBX 파일에서 M4A1_FBX_2 오브젝트를 드래그하여 하이 라키 뷰의 Gun 오브젝트의 자식으로 놓습니다 

그리고 Position 값을 0으로 맞춥니다

 

그림과 같이 총의 위치가 되면 됨니다

 

GunCtrl 스크립트 작성

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

public class GunCtrl : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
      
    }
    private void Update()
    {
        RotateGun();
    }

    //총회전 
    void RotateGun()
    {
        //카메라부터 거리를 설정
        Vector3 pos = Input.mousePosition;

        //화면의 기준으로 총의 움직임을 제한한다
        pos.x = Mathf.Clamp(pos.x, 0, Screen.width);
        pos.y = Mathf.Clamp(pos.y, 0, Screen.height);

        //카메라로 부터의 거리
        pos.z = 13.2f;
        //마우스 위치를 월드 좌표로 변환
        Vector3 view = Camera.main.ScreenToWorldPoint(pos);

        //총의 회전
        transform.LookAt(view);

    }

}

 

 

 

작성한 스크립트를 Gun 오브젝트에 드래그하여 붙입니다 

 

영상과 같이 마우스를 움직였을 때 총이 회전하면 됩니다 

 

반응형
반응형

 

유니티 엔진으로 3D 오브젝트와 2D오브젝트를 활용하여 게임을 만들어 보겠습니다 

간단한 총쏘기 게임을 만들건대 게임을 좋아하는 분들이 쉽게 만들 수 있도록 설명하도록 노력하겠습니다

 

 

 

총쏘기게임만들기

 

www.youtube.com

 

 

 

source.unitypackage
3.00MB

유니티 엔진을 열고 Scenes 파일에 있는 씬의 이름을 MainGame으로 바꿉니다 

 

 

그리고  source파일을 다운로드하여  클릭하여 파일을 엽니다 

 

 

 

 

 

 

게임 소스를 확인하고 임포트 합니다 

 

 

에셋에 파일을 임포트 한 모습을 확인할 수 있습니다 

 

 

하이 라키 신에서  Main Camera를 선택하여 아래 그림과 같이   Transform Position 값을 0, 1.6 , -6.2로 바꾸고 카메라의 각도 Field of View를 30으로 바꿉니다  

 

 

 

 

 

하이 라키 뷰에서 오른쪽 마우스 버튼을 누르고 Quad를 선택하여 생성합니다 

 

그리고 이름을 BackGround라고 바꾸고 Transform  Position 값을 0, 2.3, 9.5   Scale 값을 17, 10 , 1로 바꿉니다 

 

그리고  프로젝트 뷰에서  Materials 빈 파일을 만들고 마우스 오른쪽 버튼을 누르고 Material을 생성합니다

 

 

 

 

 

 

material의 이름을 background 라바꾸고 속성에 들어가서 Shader -> Unlit/Transparent로 바꾸고 그림 초원 2를 선택하여 넣습니다 

 

 

 

 

 속성을 바꾼 메터리얼을 선택하여  하이 라키뷰의 BackGround 오브젝트를 선택하고 메터리얼을 드래그하여 붙혀 넣습니다

 

 

그리고 다시 하이라키 뷰의 Quad를 만들고 이름을 Sky 라 바꾼 다음 BackGround를 만든 것과 같이 Material를 생성하여 Shader를 Unlit/Transparent 토하고 그림을 sky1을 붙입니다 그리고 메터 리어를 Sky 오브젝트에 연결합니다 

그리고 Transform을 0, 2.6, 10   Scale 17, 10,  1 롤 변경합니다 

 

 

 

 

 

 

Texture 폴더의 sky1 그림파일에서  Wrap Mode -> Repeat로 바꿉니다 

 

 

 

 

Scripts 폴더에서 Sky 스크립트를 생성하여 스크립트를 작성합니다 

 

Sky 스크립트 작성

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

public class Sky : MonoBehaviour
{

    float speed = 0.02f;
    // Update is called once per frame
    void Update()
    {
        float ofs = speed * Time.time;
        transform.GetComponent<Renderer>().material.mainTextureOffset = new Vector2(ofs, -1);
    }
}

 

 

 

 

 

 

 

 

 

하이 라키 뷰에서 Sky를 선택하고 Sky 스크립트를 연결합니다 

 

게임 뷰에서 화면을 16 : 9로 맞춥니다 

 

 

 

 

 

 

 

 

아래 동영상과 같이 화면이 연속적으로 움직이면 됩니다 

 

 

반응형
반응형

포토샵의 레이어로 그림파일을 만들고 그것을 이용해 이모티콘으로 제출할 GIF 파일의 움직이는 이모티콘을 만들 경우가 있습니다.

그래서 이번 시간에는 움직이는 이모티콘을 포토샵에서 어떻게 만드는지 알아보겠습니다

보통 카카오톡에서 움직이는 이모티콘 사이즈가 360 X 360을 기본으로 하고 있습니다  

그래서 먼저 이미지 사이즈를 360X360으로 잡고 이모티콘 작업을 합니다 

프레임 단위로 이미지를 포토샵 레이어에 작업을 합니다 

 

이렇게 포토샵 레이어 작업이 끝나면 window -> Timeline을 들어가서 Timeline 작업창을 엽니다.

 

 

 

Timeline 작업창이 열리면 Create Frame Animation 을 선택하고 클릭합니다

 

Timeline 창에 레이어를 하나한 등록 해야 하는데 타임라인 오른쪽의 작은 줄무늬 모양의 아이콘을 클릭하면 Make Frames From Layers를 들어가서 누릅니다. 

 

그러면 타임라인 작업창 레이어별 프레임의 그림이 등록되게 합니다 

플레이하면 레이어별 프레임 소요 시간을 설정 할수 있습니다 각 프레임 아래에 숫자를 클릭하면 소요시간을 조정할 수 있습니다

 

 

 

타임라인에서 작업이 끝나면 GIF파일로 출력을 하면 됩니다

Export -> Save for Web(Lagacy) 단축키는 Alt+Shift+Ctrl+s 를 누르면 GIF 파일을 출력할 수 있는 옵션 창이 나오게 됩니다 

 

출력 옵션 창이 나오면 출력 파일을 GIF로 설정을 하고 Save 버튼을 누릅니다 

 

 

 

 

Gif 파일을 열어보고 원하는 프레임 동작이 되는지 확인합니다

 

 

0

반응형
반응형

3D 맥스에서 모델링한 오브젝트를 유니티 엔진으로 임포트 하는 방법을 알려드리겠습니다

먼저 3D맥스에서 모델링한 오브젝트를 선택합니다 

 

그리고 선택한 오브젝트를 File -> Export -> Export Selected를 선택합니다

만약 맥스에서 작업한 모든 오브젝트를 가져오고 싶으면 윗 쪽에 있는 Export를 선택하면 됩니다

 

 

Save as type을 FBX 파일을 선택하고 이름을 파일 네임을 적고 저장합니다

 

FBX Export 옵션에서 텍스트 파일을 가져오기 위해서 Enbed Media -> Embed Media을 체크합니다

그리고 Up Axis Y-up을 선택하여야 합니다 

uniy 3D 좌표에서는 y 축을 중심축으로 움직이기 때문입니다 

그리고 ok 합니다 

 

unity 엔진으로 가서 Assets에서 오른쪽 마우스 버튼을 누르고 Import New Asset을 누릅니다

 

 

맥스에서 저장한 FBX 파일과 오브젝트의 맵핑 파일을 두 개 선택하고 Import 합니다

 

 

 

unity  Assets에서  맥스에선 변환한 FBX 파일과 맵핑 파일이 들어 왔는지 확인하고 오브젝트를 끌어서 하이라키에 올려놓으면 오브젝트와 맵핑파일이 연결되어 잘 나오는지 확인합니다 

반응형
반응형

현재 미군 제식 소총이며 5.56 X 45 탄환을 사용하며 미 콜트사에서 생산한 단축형 자동소총이다 국군 하기 분류법에 따르면 자동보총이다 M16 A2의 단축형 M4 카빈의 개량형이며 차이점으로는 3 점사가 불가해지고 완전 자동 사격이 가능해졌으며 광학장비 장착을 위한 캐링 핸들 탈착이 가능하다는 점이다 베트남전 당시 특수 부대에게 보급된 XM177에 뿌리를 두고 있으며 M727 아부다비 카빈과 M733에 영향을 받았다 

그리고 M16A2  보다 짧고 가벼우며 80% 정도의 부품들이 M16 A2와 호환된다 

M4A1은 지속적인 자동 사격시 발생하는 열을 효과적으로 발산시키기 위한 굵은 총열을 가지고 있다

 

생산연도 1994 ~현재

중량  2.68 kg

길이 838mm

총열 길이 368.3 mm

탄약  5.56 X 45 mm

연사 속도 분당 700~950 발

총구 속도 884 m/s

유효사거리 360m

 

3DMax로 개머리판 , 본체, 총열을 3개로 나누어서 모델링을 하였습니다 

모델링한 폴리콘 모습 WireFrame 모습

3 오브젝트를 합체한 모습 

 

 

 

간단하게 사진을 보고 형태를 잡고 모델링을 합니다  Default Shading

 

모델링 완성 M4A1 뒷모습 

멋지게 나왔네요 ㅎㅎ

 

 

 

약간 앞에서 튼 모습 ㅎㅎ

 

옆모습 

 

맵핑한 UV

 

유니티 에서 한번 찍어 봅니다 ㅎㅎ

대충 쓰기에 적당하네요 ㅎㅎ

 

 

멋진 회전 영상 ㅎㅎ

m4a1.zip
0.69MB

반응형
반응형

변수란 

● 테이터를 담아 두기 위한 상자

● 값이 변할 수 있음 : 변수에 저장한 값은 프로그램 중 다른 값으로 바뀔 수 있음

 

변수 만들기(선언)

 

영문과 숫자을 이용해서 작성 단 숫자로 시작 불가

한글 사용 안됨

특수 문자 사용 불가 , 단 언더바 ( _ )는 사용 가능

C#에서 이미 사용하는 키워드, 변수 이름과 중복되면 안 됨 

변수에 어떤 종류의 데이터를 저장할 것인지 지정하여야 함

데이터 타입이 다른 값을 변수에 저장하려면 에러가 발생함

 

유니티 스크립트에서 가장 많이 사용하는 기본 테이터 4가지

 

int  정수 타입

float   소수 타입

string d 문자열 타입

bool  참/거짓 논리 타입

Vector3 위치 값

 

데이터 타입 예(유니티) 

 

GameObject  

Transform  

Rigidbody   

Collider  

 

변수의 이름 짓는 법

 

변수의 이름을 지을 때는 변수에 저장할 값의 특지를 가장 정확히 표현할 수 있는 이름을 찾으려고 노력해야 한다

name, speed, score처럼 포괄적인 이름이 아닌 playerName, carSpeed, opponentScore처럼 구체적인 이름을 하는 것이 좋다

 

변수의 동작 방식

 

유니티의 빈 오브젝트를 하나 만들어서 아래와 같이 VariableTest 스크립트를 작성하고 컴포넌트를 추가한다

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

public class VariableTest : MonoBehaviour
{

    public int myNumber = 9;
    void Start()
    {
        Debug.Log(2 + 9);
        Debug.Log(11 + myNumber);
    }


    void Update()
    {
        
    }

유니티의 Play 클릭하면 아래와 같은 결과 값이 콘솔 창에 나온다

변수를 사용하면 원하는 값을 저장하거나 꺼내 쓸 수 있으며 변수 이름 그대로 사용해서 연산을 수행할 수 있다

반응형
반응형

포토샵 꿀팁을 소개합니다 

아주 유용하게 써먹을 수 있는 포토샵 기능입니다 

그림과 그림 사이의 자연스러운 연결할 수 있는 포토샵 기능을 소개합니다 

 

Rectanguler Marquee tool 사각 툴을 사용하여 연결할 부분을 드래그합니다 

그림과 같이 빈 그림과 그림의 빈 공간을 드래그합니다  

 

 

 

포토샵 윗바에 있는 

Edit -> Convert Aware Fill을 누릅니다 

그러면 아래 그림과 같이 연결할  이미지와 이미지 사이의 연결할 수 있는 설정 화면으로 이동하게 됩니다 

아래 그림과 같이 사각툴로 드래그한 부분을 이미지와 이미지 사이의 연결 부분을 미리 보기 형식으로 나타내어 주기도 합니다.

아래쪽 ok 합니다 

 

그림과 그림이 자연스럽게 연결된 모습을 확인 합니다 

그림과 그림 사이의 연결 뿐만 아니라 그림과 그림 사이의 경계선이 불규칙하게 보일 때 자연스러운 연결 그림을 원한다면 이 기능을 사용하면 원하는 작업을 얻을 수 있을 거 같습니다 

게임에서 백그라운드 그림을 작업할때 그림의 연속하여 반복되는 부분이 있는데 그럴 때 첫 그림 끝자락과 마지막 끝자락이 자연스럽게 연결되어야 백 그라운드 배경이 연속적으로 흘러갈 수 있는데 포토샵에서 이런 기능이 있어서 정말 편 한 것 같습니다 

한층 게임 만들때 수월 할 거 같습니다 ㅎㅎ

반응형
반응형

3DS MAX  모델링

K2 소총을 만들어 보았습니다 

오브젝트를 3개로 나누어 모델링 시작 ㅎㅎ

 

먼저 밑바탕으로 k2 소총의 이미지를 플랜에 덮어 씌우고 프리즈 시킨 다음 이미지를 밑바탕으로 오브젝트 3개를 생성하여 개머리판 총열 그리고 본체를 나누어서 모델링을 합니다 

 

 

다시 오브젝트 합체하여 오브젝트 화이어 키고 사진 찰칵!!!

어느정도 모델링하여 모양이 나오면 모델링을 완성시켰습니다 

더 세밀한 작업을 할수 있지만 간단하게 게임 아이템으로 사용 하기 때문에 복잡한 모델링은 패스하였습니다 

 

 

 

 

모델링한 오브젝트를 맵을 피기 위해서 UVwarp UVW 합니다  오브젝트가 굴곡이 많지 않고 메카닉 소재와 유사하여 

맵을 한 면 한면 사진 찍듯이 맵을 핍니다 

맵을 핀 맵 UV 를 PNG 파일로 저장하여 포토샵으로 옮깁니다 

아래 그림과 같이 3개의 오브젝트로 나누어진 맵을 볼수가 있습니다 

 

 

 

포토샵에서 맵 그림 작업

포토샵에서 작업한 맵을 옮겨서 실제 k2 소총을 밑 바탕으로 경계면을 실제 총과 같이 색을 테두리를 브러시 툴로 칠합니다 

그리고 끝부분을 Blur 툴을 이용하여 부드럽게 합니다 

 

대충  k2 모양이 나오 네요 ㅎㅎ

포토샵에서 작업한 맵 이미지를 맥스에서 쉐이더를 만들고 비트맵으로 오브젝트에 덮으니 진짜 리얼 k2 소총이 나오네요 ㅎㅎ

맥스 모델링 을 섬세하게 하지 않아도 맵과 쉐이더로 모양을 낼 수 있다는 것이 신기할 따름입니다 ㅎㅎ

 

랜더링 사진 찰칵 ㅎㅎ!!!

랜더링 하여 사진을 찍었는데 맵핑 작업을 좀더 섬세하게 하면 더욱 진짜 총과 같이 나올 것 같은 예감이 들기도 합니다 

조금은 아쉽지만 그럭저럭 만족합니다 

 

 

 

 

맥스모델링하고  랜더링으로 500 프레임을 주고 AVI롤 출력을 하였습니다

360 도 회전 하는 이미지를 감상하시길 바랍니다 

 

3D 맥스 모델링 한 k2 소총을  맥스 프로젝트 파일입니다 

3D 맥스 공부하시는 분들은 참고하세요 ㅎㅎ

프로젝트 파일 

k2소총.zip
2.20MB

반응형
반응형

포토샵 으로 영화 베놈을 그려 보았습니다 

 

 

팔 다리 머리 몸톰 을 레이어로 나누어  그렸습니다 

 

 

 

 

완성

반응형

+ Recent posts