728x90
의의
콘솔로만 구현하는 C#기반 슈팅게임 구현
개선점
1. 1편의 문제점은 키 입력이 존재하지 않으면, 화면이 갱신되지 않는다는 것이었다.
이 부분은 키의 입력이 존재하지 않더라도 실행이 가능하도록 Console.KeyAvailable 을 활용하여 해결하였다.
if (Console.KeyAvailable == true) // 키입력이 존재한다면
2. 키 입력 문제가 해결되었으니, 적의 움직임, 총알의 움직임, 플레이어의 움직임을
매 프레임마다 계산하여 동시에 표현할 수 있게 되었다.
프레임을 구하는 원리는 다음을 사용하였다.
const int waitTick = 1000 / 10;
int currentTick; int lastTick = 0;
while (true)// 반복
{
// 현재 시간
currentTick = System.Environment.TickCount;
// 경과 시간이 1 / 10 초 보다 작다면
if (currentTick - lastTick < waitTick)
{
continue; // 실행 건너뜀
}
else // 조건 만족 시
{
lastTick = currentTick;
// 이곳에 실행할 함수
}
}
currentTick에 매 Tick을 대입하고 마지막 Tick인 lastTick에서 뺀 값이,
미리 상수로 정한 waitTick, 즉 프레임률을 넘어가면 else이하 문을 실행하게 된다.
결과
10프레임의 주기로 슈팅게임이 실행된다.
하지만, Console.Clear()의 호출로 화면을 지우면서 갱신하기에
프레임이 높아지면 거의 백지가 보이는 현상이 발생하고,
프레임이 더 낮아지면 플레이 속도가 너무 느리다는 단점이 존재한다.
전체 코드
using System;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Threading;
/* - 프로그램 구조
1. 전체적인 콘솔 창을 15행 20열의 2차원 행렬로 본다.
2. 각 원소는 char[] 형의 pixel 이라는 이름으로 정의하고, 각 원소는 기본적으로 ' ' 공백을 담고 있다.
3. 플레이어, 적, 총알은 각 pixel의 위치에 char 형으로 삽입된다.
4. 10프레임의 주기를 가지고 연산이 자동 발생!
5. 적 플레이어도 같은 원리로 총알을 발사함 >> 피격 당하면 점수가 -100점
6. 적 플레이어 총알과 내 총알이 부딪히면 둘다 사라짐
7. 적 플레이어가 너무 한쪽 벽에 쏠리는 현상을 방지함 >> 한쪽 끝에 붙으면 반대방향으로 전환함
8. 두 플레이어 모두 총알은 한발이 어딘가에 맞기 전까지, 한발 씩만 쏠 수 있음
*/
namespace ConsoleApp2
{
class Shooting
{
static int row = 15; static int column = 20; // 표시할 행과 열의 수
static int score = 0; // 점수 저장 변수
static char[,] pixel = new char[row, column]; // 표시할 픽셀 2차원 배열
public static int[] playerPos = { 13, 9 }; // 플레이어 현 위치
public static int[] bulletPos = { playerPos[0] - 1, playerPos[1] }; // 총알의 현 위치
public static int[] enemyPos = { 3, 10 }; // 적의 현 위치
public static int[] enemyBulletPos = { enemyPos[0] + 1, enemyPos[1] }; // 적 총알 위치
public static bool isAttack;
static void Main(string[] args)
{
int width = 30; int height = 20; // 윈도우 창 사이즈 설정
const int waitTick = 1000 / 10; // 10 프레임(10FPS)
int lastTick = 0; // 마지막 틱
int currentTick; // 현재 틱
isAttack = false;
InitializeWindow(width, height); // 윈도우 창 초기화
//DrawFrame(isAttack);
while (true)// 반복
{
// 현재 시간
currentTick = System.Environment.TickCount;
// 경과 시간이 1 / 10 초 보다 작다면
if (currentTick - lastTick < waitTick)
{
continue; // 실행 건너뜀
}
else // 조건 만족 시
{
lastTick = currentTick;
if (Console.KeyAvailable == true) // 키입력이 존재한다면
{
var key = Console.ReadKey();
if (key.Key == ConsoleKey.RightArrow) // 우측 이동
{
playerPos[1] += 1;
}
if (key.Key == ConsoleKey.LeftArrow) // 좌측 이동
{
playerPos[1] -= 1;
}
if (key.Key == ConsoleKey.Spacebar) // 공격
{
if (isAttack == false) // 발사 중이 아니라면
{
isAttack = true; // 발사 가능
}
}
}
DrawFrame(isAttack); // 프레임 연산
CheckBullet(); // 적과 나의 총알 충돌 체크
}
}
}
// 윈도우 세팅
static void InitializeWindow(int width, int height)
{
Console.Title = "Console Shooting"; // 제목 설정
Console.SetWindowSize(width, height); // 창 크기 설정
Console.BackgroundColor = ConsoleColor.White; // 배경 색 설정(흰색)
Console.Clear(); // 클리어
Console.CursorVisible = false;
}
// 화면 그리기
public static void DrawFrame(bool isAttack) // 프레임 갱신
{
Console.Clear(); // 일단 전 프레임을 전부 지운다.
MoveEnemy(); // 적 이동
// 만약 적 또는 플레이어가 화면 사이즈 밖으로 벗어나게 된다면, 가장 자리에 위치를 Fix 시킴.
if (enemyPos[1] <= 0) enemyPos[1] = 0; if (enemyPos[1] >= column - 1) enemyPos[1] = column - 1;
if (playerPos[1] <= 0) playerPos[1] = 0; if (playerPos[1] >= column - 1) playerPos[1] = column - 1;
enemyBulletPos[0] += 1; // 적 총알 이동
if (isAttack == true) bulletPos[0] -= 1; // 플레이어 총알 이동
else
{
bulletPos[0] = playerPos[0] - 1; // 총알을 쏘게되면 플레이의 위치보다 1만큼 앞라인에서 출발한다.
bulletPos[1] = playerPos[1]; // 열의 위치는 일정함.
}
// 2차원 배열 순회
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
if (i == 0 && j >= 0 && j <= 19)
{
pixel[i, j] = '■';
}
else if (i == 14 && j >= 0 && j <= 19)
{
pixel[i, j] = '■';
}
else if (i == bulletPos[0] && j == bulletPos[1] && isAttack == true) // 총알 위치라면
{
pixel[i, j] = '■'; // 총알 그림 대입
}
else if(i == enemyBulletPos[0] && j == enemyBulletPos[1])
{
pixel[i, j] = '■'; // 적 총알
}
// 적의 포지션이라면
else if (i == enemyPos[0] && j == enemyPos[1])
{
pixel[i, j] = '▼'; // 적 캐릭터 저장
}
else if (i == playerPos[0] && j == playerPos[1]) // 플레이어 라면
{
pixel[i, j] = '▲'; // 플레이어 저장
}
else // 아무것도 아니면
{
pixel[i, j] = ' '; // 공백
}
}
}
// DarkBlue컬러로 점수판 생성
Console.ForegroundColor = ConsoleColor.DarkBlue;
if (score == 0)
{
Console.WriteLine("Score " + "000");
}
else
{
Console.WriteLine("Score " + score);
}
Console.ResetColor(); // 색상 초기화
Console.BackgroundColor = ConsoleColor.White; // 배경색 흰색
// 다시 배열 순회
for (int i = 0; i < row; i++)
{
for (int j = 0; j < column; j++)
{
if (i == bulletPos[0] && j == bulletPos[1] && isAttack == true) // 총알 위치라면
{
Console.ForegroundColor = ConsoleColor.Blue; // 파란색으로
Console.Write(pixel[i, j]);
Console.ResetColor();
Console.BackgroundColor = ConsoleColor.White;
}
else if (i == enemyBulletPos[0] && j == enemyBulletPos[1])
{
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.Write(pixel[i, j]);
Console.ResetColor(); // 색상 초기화
Console.BackgroundColor = ConsoleColor.White; // 배경 흰색 초기화
}
else if (i == playerPos[0] && j == playerPos[1]) // 배열의 원소가 플레이어 라면
{
Console.ForegroundColor = ConsoleColor.DarkGreen; // 청록색으로
Console.Write(pixel[i, j]); // 플레이어 원소를 그린다.
Console.ResetColor(); // 색상 초기화
Console.BackgroundColor = ConsoleColor.White; // 배경 흰색 초기화
}
else if (i == enemyPos[0] && j == enemyPos[1]) // 배열의 원소가 적이라면
{
Console.ForegroundColor = ConsoleColor.Red; // 빨간색
Console.Write(pixel[i, j]);
Console.ResetColor();
Console.BackgroundColor = ConsoleColor.White;
}
else
{
Console.Write(pixel[i, j]); // 아무것도 아니면 공백 있던거 출력
}
}
Console.WriteLine(); // 한 행 끝날때마다 개행
}
Console.SetCursorPosition(0, 0);
}
static void MoveEnemy() // 적 이동
{
Random rand = new Random();
int number = rand.Next(-1, 2); // -1, 0, 1 중 난수를 생성하고 적 위치에 합산한다.
enemyPos[1] += number; // 프레임 호출될 때마다 적은 랜덤하게 좌우로 한칸씩 이동
if (enemyPos[1] == 19) // 너무 오른쪽이나 왼쪽으로 붙지 않게 조정
{
enemyPos[1] -= 1;
}
else if (enemyPos[0] == 0)
{
enemyPos[1] += 1;
}
}
static void CheckBullet()
{
if (bulletPos[0] == enemyPos[0] && bulletPos[1] == enemyPos[1]) // 만약 총이 적에게 맞는다면
{
pixel[bulletPos[0], bulletPos[1]] = ' '; // 총알 사라짐
score += 100; // 점수는 100점 증가
Console.Beep();
bulletPos[0] = playerPos[0] - 1; // 앞으로 생성 될 총알 위치 초기화
bulletPos[1] = playerPos[1];
//pixel[enemyPos[0], enemyPos[1]] = '□'; // 적 사라짐
isAttack = false;
}
else if (bulletPos[0] <= 1) // 만약 시야를 벗어난다면
{
pixel[bulletPos[0], bulletPos[1]] = ' '; // 총알 사라짐
bulletPos[0] = playerPos[0] - 1; // 초기화
bulletPos[1] = playerPos[1];
isAttack = false;
}
else if (enemyBulletPos[0] == bulletPos[0] && enemyBulletPos[1] == bulletPos[1]) // 적 총알 == 플레이어 총알 일 때
{
pixel[enemyBulletPos[0], enemyBulletPos[1]] = ' ';
enemyBulletPos[0] = enemyPos[0] + 1;
enemyBulletPos[1] = enemyPos[1];
pixel[bulletPos[0], bulletPos[1]] = ' '; // 총알 사라짐
bulletPos[0] = playerPos[0] - 1; // 초기화
bulletPos[1] = playerPos[1];
isAttack = false;
}
if (enemyBulletPos[0] == playerPos[0] && enemyBulletPos[0] == playerPos[1]) // 적 총알 == 플레이어 일때
{
pixel[enemyBulletPos[0], enemyBulletPos[1]] = ' ';
enemyBulletPos[0] = enemyPos[0] + 1;
enemyBulletPos[1] = enemyPos[1];
if(score > 0) score -= 100; // 점수 차감
}
else if (enemyBulletPos[0] >= 14) // 적 총알 시야 이탈
{
pixel[enemyBulletPos[0], enemyBulletPos[1]] = ' ';
enemyBulletPos[0] = enemyPos[0] + 1;
enemyBulletPos[1] = enemyPos[1];
}
}
}
}
728x90
'C# 콘솔 & 윈도우폼' 카테고리의 다른 글
[C#] 콘솔창으로 만드는 텍스트RPG (0) | 2022.07.14 |
---|---|
[C#] 숫자야구 게임 구현하기 (0) | 2022.07.05 |
[C# Window Form] 윈도우 폼으로 슈팅게임 구현하기 4 : 갤러그 (2) | 2022.07.02 |
[C#] 콘솔로 슈팅게임 구현하기 3 : 갤러그 (0) | 2022.07.02 |
[C#] 콘솔 창으로 슈팅게임 구현하기 1 : 갤러그 (2) | 2022.06.30 |