[XNA] 스프라이트를 이용한 애니메이션 구현

2012. 4. 26. 23:16·IT, Smart Life

이전 XNA 관련 글 보기



2011/09/15 - [Lunarian/Smart Phone/Device] - [XNA] Wanna Make a Game? : XNA 4.0 소개, 그리고 설치


2011/10/01 - [Lunarian/Smart Phone/Device] - [XNA] XNA 기본 함수 6가지와 동작원리


2011/10/09 - [Lunarian/Smart Phone/Device] - [XNA] 이미지 로드하고 화면에 출력하기





Sprite-based Animation


  단일 이미지의 단순 화면 출력을 해봤으니, 이번에는 여러 장의 이미지를 연속으로 보여줌으로써 연속된 동작처럼 보이게 하는 스프라이트 애니메이션(Sprite-based Animation)을 시도해보겠습니다.


  스프라이트(sprite)는 연속된 이미지의 변화로 캐릭터 등에 애니메이션 효과를 나타낼 때 사용합니다. 

  2D 아케이드 게임 속에 등장하는 캐릭터 동작의 대부분은 스프라이트 애니메이션입니다.


  XNA에는 감히! 스프라이트 처리를 위해 준비된 별도의 클래스가 없기 때문에 스프라이트 처리르 위한 클래스를 직접 만들어야 된다고 합니다.(What!!? -_- )


  당연히, 한번만 쓰고 끝낼 기능이 아니기 때문에 별도의 클래스를 제작해둬야겠네요.

  아무튼 시키는대로 해봅니다.



  이번 프로젝트는 크게 아래와 같이 3개의 클래스와 불러들일 이미지들로 구성됩니다.

  





Program.cs  

프로그램 구동시 제일 먼저 불려와서 게임을 Run을 시킵니다.


Game1.cs

게임 화면의 크기, 배경색을 비롯한 게임 전반적인 환경 설정 및 초기 이니셜라이즈(Initialize() 함수)를 시킵니다. 실제적인 게임 진행에 필요한 것들을 정의하고, 이번 포스팅에서 사용하고자 하는 이미지를 로드(LoadContent())하고, 그리기 명령(Draw())을 포함하고 있습니다.


Sprite.cs

앞서 말했듯이, XNA 자체에서는 스프라이트 효과를 주는 함수가 지원되지 않기 때문에, sprite()함수를 별도로 만들고, Game1.cs에서 Upload된 컨텐츠(이미지)를 연속해서 불러서 그리는 Draw() 함수를 정의 해 둡니다.





Sprite.cs 코딩


  Sprite.cs 함수에 들어가 있는 핵심 내용은, 이미지 정보(이미지 텍스처의 개수, 테스처의 가로, 세로 크기)를 불러와서, i 번째 이미지를 출력하는 것입니다.


[ Sprite.cs 코드 보기 ]


###csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;     // for Texture 2D
using Microsoft.Xna.Framework.Content;      // for Content;


namespace WindowsGame1
{
    class Sprite
    {
        // 선언
        private List<Texture2D> m_lstTex = new List<Texture2D>();
        private Point m_size;
        private int m_cnt;


        public int Count() { return m_cnt; }
        public int Width() { return m_size.X; }
        public int Height() { return m_size.Y; }


        public Sprite()
        {
            m_size.X = 0;
            m_size.Y = 0;
            m_cnt = 0;
        }


        // 하나의 이미지만 로드할 때
        public void Load1(ContentManager cm, string sName)
        {
            Texture2D tex = cm.Load<Texture2D>(sName);
            m_lstTex.Add(tex);
            m_cnt = 1;
            m_size.X = m_lstTex[0].Width;
            m_size.Y = m_lstTex[0].Height;
        }


        // 여러장의 이미지를 연속으로 로드할 때
        public void Load(ContentManager cm, string sName, int num)
        {
            string s;
            
            for (int i = 0; i < num; i++)
            {
                s = sName + i.ToString("D4");       // 숫자를 00XX 형식으로 만든다.
                Texture2D tex = cm.Load<Texture2D>(s);
                m_lstTex.Add(tex);
            }


            m_cnt = num;
            m_size.X = m_lstTex[0].Width;
            m_size.Y = m_lstTex[0].Height;
        }


        public void Draw(SpriteBatch batch, int i, Vector2 pt, Color c)
        {
            if (i >= 0 && i < m_cnt)
            {
                batch.Draw(m_lstTex[i], pt, c);
            }
        }
    }
}




  i.ToString("D4")는 숫자 i를 00XX 형식의 문자열로 바꿔주는 역할을 합니다.

  이런 변환이 필요한 이유는 Game1.cs에서 이미지를 불러올 때, 파일명이 비행0001, 비행0002, ...., 비행0015 이렇게 되어 있기 때문입니다. 읽어들이는 이미지 파일의 파일명 형식에 맞춰준 거죠.

  그리고 'Vector2 pt'는 이미지 출력 위치 좌표를 말합니다.

[ i.ToString("D4") 예시 ]

### csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 20;
            string s = i.ToString("D4");
            Console.WriteLine(s);            
        }
    }
}









Game1.cs 코딩

  Game1 클래스에서는 Sprite를 하나 선언하고,

### csharp
Sprite sp = new Sprite();


  Sprite 클래스의 Load() 함수를 이용해서 16장의 비행00XX 이미지들을 로드합니다.

###csharp
sp.Load(Content, "비행", 15); // 16장의 이미지를 로드한다. (비행0000~비행0015)


  그리고 Sprite 클래스의 Draw() 함수를 이용해서 이미지들을 불러와서 출력하는 시간간격과 이미지의 출력 위치, 색깔 등을 정의합니다.

###csharp
sp.Draw(spriteBatch, (int)(gameTime.TotalGameTime.TotalMilliseconds / 50) % sp.Count(), new Vector2(330, 200), Color.White);

  위 코딩에서 TotalMilliseconds / (숫자) 이 부분의 숫자를 바꾸면 이미지를 그리는 시간 간격을 조절할 수 있으므로, 애니메이션 속도를 조절할 수 있습니다. Vector2( , )는 이미지를 출력할 위지(position)을 나타냅니다. 그리고 Color.(White)에서 색깔 부분을 바꾸면 이미지에 지정한 색을 입히게 됩니다.

왼쪽 부터, Color.White / Color.Aqua / Color.Pink




[ Game1.cs 코드 보기 ]

### csharp
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Sprite sp = new Sprite();
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredBackBufferWidth = 800;
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here


            base.Initialize();
        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            sp.Load(Content, "비행", 15); // 16장의 이미지를 로드한다. (비행0000~비행0015)


            // TODO: use this.Content to load your game content here
        }


        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();


            // TODO: Add your update logic here


            base.Update(gameTime);
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);


            // TODO: Add your drawing code here


            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
            sp.Draw(spriteBatch, (int)(gameTime.TotalGameTime.TotalMilliseconds / 50) % sp.Count(), new Vector2(330, 200), Color.White);
            spriteBatch.End();
            base.Draw(gameTime);
        }
    }
}





F5 : Start Debugging

  '안돌아가는 훌륭한 프로그램보다, 돌아가는 조악한 프로그램이 낫다.'라는 말이 있죠.
  아무렇게 코딩해도 좋다는 말은 아니지만, 그래도 역시 디버깅을 했을 때, 에러가 나는 것 보다 돌아가는 걸 보는 게 훨씬 기분 좋습니다. ^__^


  우선, 16장의 이미지를 그대로 다 사용한 경우입니다.
  비행기가 좌우로 반복해서 롤링(Rolling)을 하는데, 이상하게 다시 중심으로 올때, 끊기는 것 같습니다.
  소스로 사용하신 이미지들을 한장씩 보시면, 이미지 첫장과 이미지 마지막장이 거의 동일한 비행기 이미지를 나타내는데, 이때문에 애니메이션이 약간 부자연스러워 보입니다.



이미지 16장을 모두 사용한 경우의 결과



  아래는 제일 마지막 이미지(비행0015)를 삭제하고, 코딩도 15장만 불러들이도록 수정한 결과 화면입니다.

  완전히 만족할 수는 없지만, 그래도 조금 더 낫네요~ ^____^ 

  자연스럽게 연결되는 애니메이션 소스가 중요하다는 것과, 반복문을 돌릴 때, 첫 이미지와 마지막 이미지가 동일하면, 동일한 화면을 두번 그리게 되니까 조심해야 된다는 교훈을 얻었네요~




마지막 이미지를 제외한 15장의 이미지를 사용한 경우의 결과



  시간이 많이 없어 실습이 더디지만, 재밌네요 ^__^

  

  다음은 스프라이트 애니메이션을 구현하는 코드를 다른 이미지 소스에도 계속 사용할 수 있도록,

  스프라이트 매니저 클래스를 만들어 보겠습니다!! 




SPACECHILD_XNA_Proto1.zip




Reference // 톡톡튀는 XNA를 이용한 단계별 슈팅게임 만들기

톡톡튀는 XNA를 이용한 단계별 슈팅게임 만들기
국내도서>컴퓨터/인터넷
저자 : 최창수,서정만
출판 : 도서출판정일 2011.08.25
상세보기


저작자표시 비영리 변경금지 (새창열림)

'IT, Smart Life' 카테고리의 다른 글

[Android] Nexus S, 아이스크림 샌드위치(안드로이드 4.0) 업데이트  (573) 2012.05.07
[Kinect] PC연결을 위한 'Kinect for XBOX360' USB어댑터(AC Adapter/Power Supply/USB)  (592) 2012.05.06
AForge.NET FRAMEWORK : 유전알고리즘을 C#으로  (6) 2012.03.09
[Android] 이클립스 갈릴레오 버전 시, ADT 업데이트 에러 문제  (892) 2012.01.11
'IT, Smart Life' 카테고리의 다른 글
  • [Android] Nexus S, 아이스크림 샌드위치(안드로이드 4.0) 업데이트
  • [Kinect] PC연결을 위한 'Kinect for XBOX360' USB어댑터(AC Adapter/Power Supply/USB)
  • AForge.NET FRAMEWORK : 유전알고리즘을 C#으로
  • [Android] 이클립스 갈릴레오 버전 시, ADT 업데이트 에러 문제
스페이스차일드
스페이스차일드
우주공학, IT, 그리고 다가오는 미래에 대해서 이야기합니다.
  • 스페이스차일드
    스페이스차일드
    스페이스차일드
  • 전체
    오늘
    어제
    • 분류 전체보기 (404)
      • SPACECHILD (158)
      • IT, Smart Life (155)
      • WEB DEV (10)
      • 러닝, 마라톤 (25)
      • 세계일주 (0)
      • ETC. (55)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 대한민국 우주산업 실태조사 보고서 (2008-2019)
    • 우리나라 5대 항공우주박물관
    • [용어정리] 항공우주공학 : 우주파트를 중심으로
  • 인기 글

  • 태그

    spacex
    구입
    우주개발
    추천
    아이폰
    스마트폰
    마라톤
    안드로이드
    게임
    맥
    mac
    우주산업
    달리기
    다운로드
    스페이스x
    애플
    후기
    nasa
    갤럭시
    애플워치
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
스페이스차일드
[XNA] 스프라이트를 이용한 애니메이션 구현
상단으로

티스토리툴바