25.10.08 개발일지 / C# T-Rex Endless Runner - 3 / 주요 메서드 정리 및 영상

2025. 11. 13. 14:01·LMS 7/개발일지

주요 메서드 분석

1. 생성자

public Form1()
{
    InitializeComponent(); // 도구 초기화
    GameSetUp(); // 게임 설정
    Reset(); // 게임 초기화
    //this.KeyPreview = true; // 방향키 이벤트를 폼에서 먼저 받도록 설정
}

> InitializeComponent() 는 WinForms 생성시 처음부터 들어가있는데, 도구들을 모두 초기화하는 역할을 함

> GameSetUp()은 사용자 정의 메서드임

> Reset()은 사용자 정의 메서드임

> this.KeyPreview = true 는 포커스를 Form으로 가게 만들도록 하는데, 데스크톱은 이게 없어도 포커스가 Form으로 가있는 반면 노트북은 아니라서 필수적으로 해줘야 함(안하면 방향키 입력을 Form이 아닌 Label, PictureBox 등이 받아버림)

 

2. KeyDown 이벤트

> KeyDown += KeyIsDown;

private void KeyIsDown(object sender, KeyEventArgs e)
{
    if (!gameover)
    {
        if(e.KeyCode == Keys.Up && !jumping)
        {
            jumping = true; // 점프 상태로 변경
        }
        if(e.KeyCode == Keys.Down && !jumping)
        {
            if(changeAnim == false)
            {
                trex.Image = Properties.Resources.dino_crouch; // 티렉스 이미지 웅크리기로 변경
                changeAnim = true; // 애니메이션 변경 상태 변경
            }

            trex.Top = 352; // 티렉스 위치 변경
        }
    }
}

> 키가 눌렸을 때 발생하는 이벤트임

> Keys.Up(위 방향키)인 경우는 점프(trex 움직임은 bool jumping 을 가지고 GameTimerEvent()에서 구현함)

▲ Keys.Down(아래 방향키)인 경우 엎드리기(Resource에 있는 dino_crouch를 사용하여 이미지를 변경함 => bool changeAnim도 변경)

 

3. KeyUp 이벤트

> KeyUp += KeyIsUp;

private void KeyIsUp(object sender, KeyEventArgs e)
{
    if(e.KeyCode == Keys.Down && !gameover)
    {
        trex.Image = Properties.Resources.dino_run; // 티렉스 이미지 달리기로 변경
        trex.Top = 313; // 티렉스 위치 변경
        changeAnim = false; // 애니메이션 변경 상태 변경
    }
    if(e.KeyCode == Keys.Enter && gameover)
    {
        Reset(); // 게임 초기화
    }
}

> KeyDown이 눌렸을 때라면 KeyUp은 떼어졌을 때 발생하는 이벤트임

▲ Keys.Down(아래 방향키)가 떼어졌을 때 Resources에 있는 dino_run(초기상태)로 다시 변경, bool changeAnim도 변경함

> Keys.Enter(엔터키)와 bool gameover인 경우에만 reset()함

 

4. Paint 이벤트

> Paint += FormPaintEvent;

private void FormPaintEvent(object sender, PaintEventArgs e)
{
    Graphics Canvas = e.Graphics; // e.Graphics 전달ㅎ

    // 배경 이미지 그리기
    Canvas.DrawImage(backgroundImage, bgPositionX, bgPositionY, backgroundWidth, backgroundHeight);
    Canvas.DrawImage(backgroundImage2, bg2PositionX, bgPositionY, backgroundWidth, backgroundHeight);

    // 부드러운 그래픽 설정
    Canvas.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

    // 고품질 이미지 설정
    Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
}

> Graphics Canvas = e.Graphics는 프로퍼티 get을 사용하는 방법임(▼ int로 예를 든 것 참고)

class MyClass{
    private int myField;
    public int MyField{
        get{
            return myField;
        }
        set{
            myField = value;
        }
    }
}

MyClass obj = new MyClass();
obj.MyField = 3; // set
int yourNum = obj.MyField; // get

> DrawImage는 Image 객체와 특정 위치, 특정 너비와 높이를 가지고 이미지를 그림.

> SmoothingMode는 안티에일리어싱(외곽선을 부드럽게 하는 속성) 기능

> InterpolationMode는 이미지 확대 및 축소시 부드러움을 추가하는 기능(DrawImage에서 실제 사진보다 크거나 작게 그려지면 픽셀이 구겨지는데 이를 해결함)

 

5. Tick 이벤트

> Tick += GameTimerEvent;

private void GameTimerEvent(object sender, EventArgs e)
{
    MoveBackgrounds(); // 배경 이동
    this.Invalidate(); // 폼 다시 그리기

    MoveObstacles(); // 장애물 이동

    lblScore.Text = "Score: " + score; // 점수 표시

    // trex를 기준으로 피격 박스 위치 조정
    hitBox.Left = trex.Right - (hitBox.Width + 20);
    hitBox.Top = trex.Top + 5;

    if (jumping)
    {
        trex.Top -= speed; // 위로 이동

        if(trex.Top < 150) // 최고점 도달
        {
            speed = -10; // 아래로 이동
        }
        if(trex.Top > 312) // 최저점 도달
        {
            jumping = false; // 점프 상태 해제
            trex.Top = 313; // 위치 초기화
            speed = 10; // 속도 초기화
        }
    }

    foreach(PictureBox x in obstacles)
    {
        if (x.Bounds.IntersectsWith(hitBox.Bounds))
        {
            GameTimer.Stop();
            trex.Image = Properties.Resources.dead;
            gameover = true;
            hitSound = new SoundPlayer(Properties.Resources.hit);
            hitSound.Play();
            trex.Top = 313;
            lblScore.Text = "press Enter to Restart";
        }
    }
}

> Tick은 interval(15)마다 이벤트를 발생시킴

> MoveBackgrounds() / MoveObstacles()를 호출함(아래 참고)

> 점수표시 및 히트박스 초기화

​

> Invalidate()는 다시 그리기를 요청하는 기능(이 경우 Paint 이벤트가 다시 호출됨)

​

> KeyDown 이벤트에서 바뀐 jumping을 가지고 실제 점프를 구현

: speed(여기선 10)만큼 trex를 위로 이동시킴

최고점(149)인 경우 speed를 -10으로 바꿔 다시 아래로 이동시킴

최저점(313)인 경우는 바닥을 뜻하므로 jumping을 변경하고, 위치와 speed도 초기화시킴

​

> foreach~ (모든 장애물들 반복)

: 장애물의 사각형 범위(x.Bounds)가 히트박스 사각형 범위(hitbox.Bounds)와 겹친 경우(IntersectsWith)

GameTimer(타이머)는 멈춤(Stop)

▼ trex 이미지는 dead로 변경

hitSound는 Resource에 저장되어 있는 hit 소리를 가져와 들려줌(Play)

lblScore 출력문 변경

 

6. GameSetUp()

> 생성자에서 처음이자 마지막으로 호출되는 메서드임

> 배경화면 및 장애물, 점수라벨, 피격박스 등을 초기화하는데 사용됨

private void GameSetUp()
{
    // 배경화면 초기화
    backgroundWidth = 900;
    backgroundHeight = 450;
    // 첫번째 배경은 backgroundWidth의 0 위치에, 두번째 배경은 backgroundWidth의 너비 위치에(끝에)
    bg2PositionX = 900;
    backgroundImage = Properties.Resources.bgPixels;
    backgroundImage2 = Properties.Resources.bgPixels;
    
    // 더블버퍼 >> 애니메이션 부드럽게(깜빡임 제어)
    this.DoubleBuffered = true;
    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

    // 폼 너비
    // Form에서 실제 컨트롤들이 놓일 수 있는 크기 중 너비(Width)값
    formWidth = this.ClientSize.Width;

    // 폼 배경색
    this.BackColor = Color.Black;

    // 장애물 리스트에 장애물 추가
    obstacles.Add(obstacle1);
    obstacles.Add(obstacle2);
    obstacles.Add(obstacle3);

    // 점수 Lable 설정
    newFont.AddFontFile("font/pixel.ttf"); // 폰트 파일 추가
    lblScore.Font = new Font(newFont.Families[0], 30, FontStyle.Bold); // 폰트 크기 및 굵기
    lblScore.ForeColor = Color.White; // 폰트 색상
    lblScore.BackColor = Color.Transparent; // 폰트 배경색
    lblScore.Text = "Score: 0"; // 초기 점수

    // 피격 박스 설정
    hitBox.BackColor = Color.Transparent;
    hitBox.Width = trex.Width / 2;
    hitBox.Height = trex.Height - 10;

    this.Controls.Add(hitBox); // 폼에 피격 박스 추가
    //hitBox.BringToFront(); // 피격 박스를 맨 앞으로
    gameover = false; // 게임 오버 초기화

    attackR = random.Next(12, 20); // 피격 타이머 랜덤 설정
}

> bgPositonX는 0으로 초기화되어 있음(필드 선언과 동시에 초기화), bg2PositionX가 900인 이유는 너비가 900인 backgroundImage 뒤에 backgroundImage2를 그대로 붙이기 위함임

> DoubleBuffered와 SetStyle(ControlStyles.OptimizedDoubleBuffer, true)는 Invalidate() 등으로 호출되는 Paint 이벤트가 배경을 지우고 다시 그리는 과정에서 깜빡이는 것을 없애주어 매끄럽게 보이기 위해 사용한다고 함

 

▲ ClientSize.Width는 실제 컨트롤들이 놓일 수 있는(실질적으로) 너비를 의미함

> 점수 라벨에 들어갈 문자에 대한 폰트설정은 resources 중 pixel.ttf를 사용함

 

▲ 히트박스는 trex의 넓은 히트박스를 좁게 만들어 줌

> attackR 은 attckTimer와 함께 박쥐(obstacle3)를 움직일 때 사용(attckR == attckTimer 인 경우 flyingAttck을 활성화하고, flyingAttack인 경우에만 obstacle3를 이동시킴)

 

7. Reset()

> 생성자 및 KeyUp 중 Keys.Enter인 경우 호출되는 메서드

private void Reset() // 게임 초기화
{
    trex.Image = Properties.Resources.dino_run; // 티렉스 이미지 달리기로 초기화
    trex.Top = 313; // 티렉스 위치 초기화

    // 장애물 위치 랜덤 설정
    obstacle1.Left = formWidth + random.Next(100, 200); // 장애물1 위치 초기화
    obstacle2.Left = obstacle1.Left + random.Next(600, 800); // 장애물2 위치 초기화
    obstacle3.Left = obstacle2.Left + random.Next(200, 400); // 장애물3 위치 초기화

    GameTimer.Start(); // 게임 타이머 시작
    score = 0; // 점수 초기화
    attackTimer = 0; // 피격 타이머 초기화
    speed = 10; // 속도 초기화
    gameover = false; // 게임 오버 초기화
    changeAnim = false; // 애니메이션 변경 초기화
    jumping = false; // 점프 초기화
    cactusSpeed = 10; // 선인장 속도 초기화
}​

> trex 초기화

> obstacle들 초기화

> 타이머 이벤트(GameTimer) 시작(Start)

> 필드 초기화

8. MoveBackgrounds()

> 배경화면을 왼쪽으로 이동시키는 메서드

private void MoveBackgrounds()
{
    // 배경 왼쪽으로 이동
    bgPositionX -= 1; // 0부터 1단위로 감소
    bg2PositionX -= 1; // 900부터 1단위로 감소

    // 배경이 화면 밖으로 나가면 이전 배경의 오른쪽에 위치시킴
    if (bgPositionX < -backgroundWidth)
    {
        bgPositionX = bg2PositionX + backgroundWidth;
    }
    if(bg2PositionX < -backgroundWidth)
    {
        bg2PositionX = bgPositionX + backgroundWidth;
    }
}

> backgroundImage와 backgroundImage2를 -1씩(왼쪽으로) 이동시킴

> backgroundImage의 위치(bgPositonX)가 -900만큼(backgroundWidth는 900임) 왼쪽으로 이동할 경우 뒤 따라오는 이미지(backgroundImage2) 뒤로 초기화시킴

9. MoveObstacles()

private void MoveObstacles()
{
    plusOneSound = new SoundPlayer(Properties.Resources.plusone); // 점수 획득 사운드

    if (!flyingAttack)
    {
        obstacle1.Left -= cactusSpeed; // 장애물1 이동
        obstacle2.Left -= cactusSpeed; // 장애물2 이동
    }
    else
    {
        obstacle3.Left -= cactusSpeed; // 장애물3(박쥐) 이동
    }

    if(attackTimer == attackR)
    {
        flyingAttack = true; // 박쥐 공격 상태로 변경
        attackR = random.Next(12, 20); // 피격 타이머 랜덤 설정
    }

    if(attackTimer == 0)
    {
        flyingAttack = false; // 박쥐 공격 상태 해제
    }

    if(obstacle1.Left < -100)
    {
        obstacle1.Left = obstacle2.Left + obstacle2.Width + formWidth + random.Next(100, 300); // 장애물1 위치 초기화
        attackTimer += 1;
        score += 1; // 점수 증가
        plusOneSound.Play(); // 점수 획득 사운드 재생
    }
    if(obstacle2.Left < -100)
    {
        obstacle2.Left = obstacle1.Left + obstacle1.Width + formWidth + random.Next(100, 300); // 장애물2 위치 초기화
        attackTimer += 1;
        score += 1; // 점수 증가
        plusOneSound.Play(); // 점수 획득 사운드 재생
    }
    if(obstacle3.Left < -100)
    {
        obstacle3.Left = formWidth + random.Next(300, 400); // 장애물3 위치 초기화
        obstacle3.Top = flyingPosition[random.Next(flyingPosition.Length)]; // 장애물3 위치 랜덤 설정
        attackTimer -= 1;
        score += 1; // 점수 증가
        plusOneSound.Play(); // 점수 획득 사운드 재생
    }

    if (score >= 10) cactusSpeed = 12;
    if (score >= 20) cactusSpeed = 14;
    if (score >= 40) cactusSpeed = 17;
    if (score >= 70) cactusSpeed = 20;
    if (score >= 100) cactusSpeed = 25;
}

> flyingAttack이 True가 되는 조건은 attackTimer과 attackR이 같아지는 경우 뿐임

attackTimer는 obstacle1, 2를 통과했을 때 +1되고, obstacle3을 통과했을 때 -1이 됨

GameSetUp()에서 12부터 19까지의 난수로 초기화된 attackR과 함께 처음 조건문이 실행된다

obstacle3이 계속 이동하여 attackTimer가 0이 되서야 flyingAttack이 0인 조건이 발생하고 obstacle1, 2가 정상적으로 왼쪽으로 이동함

> 장애물은 cactusSpeed 필드를 통해 속도가 조절되고, 위치가 -100이 되면 위치가 랜덤하게 초기화됨

> score에 따라 cactusSpeed를 증가시킴(난이도 조절)


참고

전체코드 : https://github.com/mmmmz986/T-Rex-Endless-Runner.git

참고영상(가이드) : https://www.youtube.com/watch?v=OeUj0RwzV34

 

'LMS 7 > 개발일지' 카테고리의 다른 글

25.10.13 개발일지 / C# 명코파크 (1일차)  (0) 2025.11.13
25.10.09 개발일지 / C# TRex - TCP IP 네트워크 구상  (0) 2025.11.13
25.10.07 개발일지 / C# T-Rex Endless Runner - 2 / 이벤트, 필드, 메서드 등 멤버 정의  (0) 2025.11.13
25.10.06 개발일지 / C# T-Rex Endless Runner - 1 / 프로젝트 생성 및 디자인 배치  (0) 2025.11.11
25.10.02 개발일지 / C# 4(2) (Chapter20~22)  (0) 2025.11.11
'LMS 7/개발일지' 카테고리의 다른 글
  • 25.10.13 개발일지 / C# 명코파크 (1일차)
  • 25.10.09 개발일지 / C# TRex - TCP IP 네트워크 구상
  • 25.10.07 개발일지 / C# T-Rex Endless Runner - 2 / 이벤트, 필드, 메서드 등 멤버 정의
  • 25.10.06 개발일지 / C# T-Rex Endless Runner - 1 / 프로젝트 생성 및 디자인 배치
m_Dev
m_Dev
  • m_Dev
    m_Dev
    m_Dev
  • 전체
    오늘
    어제
    • 분류 전체보기
      • MAIN STUDY
        • 정보보안
        • 빅데이터
        • 정보처리
        • 컴퓨터 구조
        • 기타
      • JOB
        • Study
        • Project
      • LMS 7
        • 개발일지
      • FRAMEWORK
        • Qt
        • MFC
        • Winform
        • WPF
        • MAUI
      • NETWORK
        • Study
        • Assignment
      • PYTHON
        • Set
        • Study
        • Assignment
        • Project
      • C
        • Set
        • Study
        • Assignment
        • Project
      • C++
        • Set
        • Study
        • Assignment
        • Project
      • C#
        • Set
        • Study
        • Assignment
        • Project
      • DATABASE
        • MySQL
        • SQLite
      • IDE
        • VisualStudioCode
        • VisualStudio
        • Pycharm
        • Colab
      • 기타
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
m_Dev
25.10.08 개발일지 / C# T-Rex Endless Runner - 3 / 주요 메서드 정리 및 영상
상단으로

티스토리툴바