2025. 10. 9. 19:32ㆍ개발

전체코드
;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace SnakeGame
{
enum Direction
{
UP,
DOWN,
LEFT,
RIGHT
}
class Snake
{
//뱀 위치 배열
static int[,] snake = new int[50, 2];
//뱀 길이
static int snakeLength = 1;
//뱀 방향
static Direction direction = Direction.RIGHT;
//먹이 좌표->랜덤 생성
static int foodX, foodY;
//게임판 크기
static int width = 20;
static int height = 20;
//게임 오버 상태
static bool gameOver = false;
static void Main(string[] args)
{
InitGame();
while (!gameOver)
{
Input();
Logic();
Draw();
Thread.Sleep(150);
}
Console.Clear();
Console.WriteLine("Game Over");
}
static void InitGame()//초기세팅
{
width = 20;
height = 20;
//뱀 머리 위치 설정
snake[0, 0] = width / 2;
snake[0, 1] = height / 2;
//먹이 위치 설정
Random random = new Random();
foodX = random.Next(0, width);
foodY = random.Next(0, height);
}
static void Input() //방향키
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.W:
Snake.direction = Direction.UP;
break;
case ConsoleKey.S:
Snake.direction = Direction.DOWN;
break;
case ConsoleKey.A:
Snake.direction = Direction.LEFT;
break;
case ConsoleKey.D:
Snake.direction = Direction.RIGHT;
break;
}
}
}
static void Logic()//뱀 이동+충돌검사+먹이처리
{
//몸통 이동
for (int i=snakeLength-1; i>0; i--)
{
snake[i,0]=snake[i-1,0]; //x좌표
snake[i,1]=snake[i-1,1]; //y좌표
}
if (direction == Direction.RIGHT)
{
snake[0,0]++;
}
else if (direction == Direction.LEFT)
{
snake[0,0]--;
}
else if (direction == Direction.UP)
{
snake[0,1]--;
}
else if (direction == Direction.DOWN)
{
snake[0,1]++;
}
//만약 벽 충돌이라면 게임오버
if (snake[0,0]<0 || snake[0,0]>=width||snake[0,1]<0||snake[0,1]>=height)
{
gameOver = true;
}
//아니라면 자기 몸을 충돌했다면 게임오버
else
{
for (int i = 1; i < snakeLength; i++)
{
if (snake[0,0] == snake[i,0]&&snake[0,1] == snake[i,1])
{
gameOver = true;
break;
}
}
}
//먹이처리
if (snake[0,0] == foodX && snake[0,1] == foodY)
{
snakeLength++; // 뱀 길이 증가
Random random = new Random();
foodX = random.Next(0, width); // 새 먹이 X
foodY = random.Next(0, height); // 새 먹이 Y
}
}
static void Draw()//화면출력
{
Console.Clear();
for (int i = 0; i < height; i++) // 행
{
for (int j = 0; j < width; j++) // 열
{
bool printed = false;
// 머리 출력
if (i == 0 || i == height - 1 || j == 0 || j == width - 1)
{
Console.Write("#");
printed = true;
}
else if (i == snake[0,1] &&j == snake[0,0])
{
Console.Write("0");
printed = true;
}
else
{
// 몸통 출력
for (int k = 1; k < snakeLength; k++)
{
if (i == snake[k,1] && j == snake[k,0])
{
Console.Write("o");
printed = true;
break;
}
}
}
// 먹이 출력
if (!printed && i == foodY && j == foodX)
{
Console.Write("*");
printed = true;
}
// 빈 칸
if (!printed)
Console.Write(" ");
}
Console.WriteLine();
}
}
}
}
개념정리가 안되서 뒤로 갈 수록 너무 헷갈린다... 그래서 개념정리 하는데 그걸 응용까지 하려니 더 헷갈린다ㅜㅜ
이걸 우리가 대체 언제 배웠냐구ㅜㅜㅜㅜ
트러블 슈팅
문제점: 스네이크 게임 메인 게임루프 while문에서 조건 고민
while(true)로 하면 루프 안에서 break; 쓰기 전에 무한반복이고,
while(!gameOver)를 쓰면 게임이 끝나지 않았을 때만 반복된다.
while (true)
{
MoveSnake();
CheckCollision();
if (충돌발생)
break; // 직접 루프를 끊어야 함
}
while (true) 버전
➡ 단점:
게임이 끝났는지 여부를 루프 안에서
직접 break로 관리해야 함.
while (!gameOver)
{
MoveSnake();
CheckCollision();
}
while(!gameOver)버전
같은 함수 안에서 gameOver=true;
만 해주면 루프가 자동으로 종료됨
해결:
!gameOver는 “게임이 진행 중일 때만 반복” 이라는 의미로,
코드의 가독성과 관리가 훨씬 깔끔하다.
트러블 슈팅 2
static void Input() //방향키
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.W:
Snake.direction = Direction.UP;
break;
case ConsoleKey.S:
Snake.direction = Direction.DOWN;
break;
case ConsoleKey.A:
Snake.direction = Direction.LEFT;
break;
case ConsoleKey.D:
Snake.direction = Direction.RIGHT;
break;
}
}
}
문제점
여기 switch문 안에 조건을 key로 하니까 에러났다.
Console.ReadKey()는 ConsoleKeyInfo구조체를 반환한다.
실제 키 값은 key.key 속성에 들어있다.
해결:
switch문의 조건을 key.key로 바꾸니까 해결 되었다.
오늘 알게 된 중요한 것!!
전역변수: 클래스 안에서 정의되고, 모든 매서드에 접근 가능함
| 지역변수 | 메서드 안 | 선언된 메서드 안 | 메서드 실행 동안 |
| 전역변수 | 클래스 안 | 클래스 내 모든 메서드 | 클래스 존재 동안 |
그러면 앞에다가 static 을 붙이면 전역변수가 될까?
답은 no!!!
1️⃣ static이란?
- **정적(static)**이라는 뜻
- 클래스에 속하고 모든 인스턴스가 공유함
- 클래스가 메모리에 올라갈 때 한 번만 만들어짐
- 인스턴스 없이 접근 가능
class Program
{
static int count = 0; // static 변수
static void Main()
{
count++; // 클래스 전체에서 공유됨
Console.WriteLine(count);
}
static void AnotherMethod()
{
count++; // 같은 count를 공유
Console.WriteLine(count);
}
}전역변수가 무조건 static은 아님. 인스턴스 필드도 전역처럼 쓸 수 있지만, 각 객체마다 별도 존재함
2️⃣ 차이점 요약
| static | 전역변수 (클래스 필드) | |
| 존재 방식 | 클래스에 속함 | 클래스에 속함 |
| 인스턴스 | 없어도 접근 가능 | 인스턴스 변수면 객체 필요 |
| 공유 | 모든 메서드/객체 공유 | 객체마다 다를 수 있음 |
| 용도 | 한 번만 만들어서 공유하고 싶을 때 | 메서드 전체에서 데이터 공유하고 싶을 때 |
즉:
- static 변수 = 전역처럼 공유 가능한 변수
- 전역변수 = 클래스 안 어디서든 접근 가능한 변수
- static 전역변수는 완전히 “진짜 전역”처럼 사용할 수 있음.
'개발' 카테고리의 다른 글
| 두 번째 팀프로젝트 [TextRPG/프로그래밍기초주차] (0) | 2025.10.14 |
|---|---|
| C#으로 블랙잭 게임 만들기 (0) | 2025.10.13 |
| Text RPG 개인과제 완료 (0) | 2025.10.01 |
| Text RPG 개발-2 (0) | 2025.09.30 |
| C#문법 종합 개인과제 [도전! 던전 탈출] (1) | 2025.09.29 |