티스토리 뷰

지금까지 만든 클래스들을 활용하여 스타크래프트 게임을 한 판 해보겠습니다.

게임의 시작과 종료를 알리는 함수를 각각 정의하고 시작하겠습니다. 스타크래프트에서는 게임을 하다가 도저히 상대방을 이길 수 없다고 판단되면 졌다는 의미로 채팅창에 gg (good game) 를 입력하고 퇴장하는 것이 예의입니다.

... # 클래스 생략

# 게임 시작
def game_start():
    print("[알림] 새로운 게임을 시작합니다.")

# 게임 종료
def game_over():
    print("Player : gg") # good game
    print("[Player] 님이 게임에서 퇴장하셨습니다.")

 

게임을 시작함과 동시에 마린 3기와 탱크 2기, 그리고 레이스 1기를 만들어보겠습니다. 객체 이름은 편의상 각 유닛의 이름 첫 글자와 숫자로 정의하겠습니다. 마린의 경우 Marine 의 m 과 숫자를 더한 m1, m2, m3 으로 합니다.

# 게임 시작
game_start()

# 마린 3기 생성
m1 = Marine()
m2 = Marine()
m3 = Marine()

# 탱크 2기 생성
t1 = Tank()
t2 = Tank()

# 레이스 1기 생성
w1 = Wraith()

 

총 6기의 유닛이 만들어졌는데 어딘가로 이동을 하거나 공격을 할 때 한꺼번에 처리하도록 하기 위해 이들을 리스트로 관리 하겠습니다. attack_units 라는 이름으로 리스트를 만들고 각 유닛들을 추가합니다.

# 유닛 일괄 관리 (생성된 모든 유닛 append)
attack_units = []
attack_units.append(m1)
attack_units.append(m2)
attack_units.append(m3)
attack_units.append(t1)
attack_units.append(t1)
attack_units.append(w1)

 

어느 정도 유닛이 모였으니 적군을 공격하러 가볼까요?

1시 방향으로 모든 유닛을 이동하겠습니다. 모든 유닛들은 Unit 클래스를 상속받았으므로 Unit 클래스의 move() 메소드를 사용할 수 있으며, 또한 모든 유닛들을 리스트로 관리하고 있기 때문에 반복문을 이용하여 수월하게 이동이 가능합니다.

# 전군 이동
for unit in attack_units:
    unit.move("1시")

 

 

적군으로 이동을 하는 와중에 탱크의 시즈 모드 개발이 완료되었다고 가정하겠습니다. 탱크 자체도 굉장히 강하지만 시즈모드의 화력은 그보다 더 무시무시하기 때문에 반드시 개발이 필요하지요. Tank 클래스에 선언된 클래스 변수 siege_developed 는 Tank.siege_developed 를 통해 접근할 수 있으며 값을 True 로 설정해주겠습니다.

# 탱크 시즈모드 개발
Tank.siege_developed = True
print("[알림] 탱크 시즈 모드 개발이 완료되었습니다.")

 

자 이제 적군이 눈앞에 있습니다. 전쟁 직전에 각 유닛들이 가진 특수 기술을 사용하여 보다 강력한 공격을 할 텐데요. 마린은 스팀팩, 탱크는 시즈모드, 레이스는 클로킹모드를 각각 사용해야 합니다. 리스트로 관리되는 유닛들은 서로 다른 기술을 사용해야 하는데 이들을 구분하기 위해서 isinstance() 를 활용할 수 있습니다.

isinstance(객체, 클래스)

이렇게 하면 객체가 특정 클래스의 인스턴스인지 여부를 확인할 수 있는데요. 예제에서는 각 유닛 객체들이 Marine 클래스의 인스턴스인지, Tank 또는 Wraith 클래스의 인스턴스인지 여부를 확인하여 각 유닛에 맞는 특수 기술을 사용하도록 합니다.

# 공격 모드 준비 (마린 : 스팀팩, 탱크 : 시즈모드, 레이스 : 클로킹)
for unit in attack_units:
    if isinstance(unit, Marine): # Marine 의 인스턴스이면 스팀팩
        unit.stimpack()
    elif isinstance(unit, Tank): # Tank 의 인스턴스이면 시즈모드
        unit.set_siege_mode()
    elif isinstance(unit, Wraith): # Wraith 의 인스턴스이면 클로킹
        unit.cloaking()

 

 

준비가 완료되었습니다. 전군 공격 명령을 통해 1시 방향으로 공격을 시도하겠습니다. 부모 클래스인 AttackUnit 의 attack() 메소드를 활용합니다.

# 전군 공격
for unit in attack_units:
    unit.attack("1시")

 

전쟁을 하는 과정에서 우리 유닛들도 피해를 입습니다. Unit 클래스의 damaged() 메소드를 호출하는데 피해 데미지는 랜덤으로 5 에서 20 사이의 값으로 지정하겠습니다. 랜덤 모듈을 사용하기 위해 소스코드 첫 줄에 import 도 해주도록 합니다.

from random import *

... # 생략

# 전군 피해
for unit in attack_units:
    unit.damaged(randint(5, 20)) # 공격은 랜덤으로 받음 (5 ~ 20)

 

그런데 예상보다 우리 유닛들의 피해가 너무 크네요. 적군이 대비를 철저히 하여 우리 유닛들은 모두 장렬히 전사하였다고 가정하겠습니다. 본진에도 더 싸울 유닛은 없고 이대로 적군이 밀려온다면 승산이 없겠네요. 아쉽지만 패배를 인정하고 gg 를 선언 후 게임에서 나가도록 하겠습니다. 게임 종료가 되는 것이죠.

# 게임 종료
game_over()

 

게임 시작부터 종료까지 수행한 동작을 순차적으로 정리해보겠습니다.

  1. 게임 시작
  2. 유닛 생성 (마린 3기, 탱크 2기, 레이스 1기)
  3. 전군 1시 방향으로 이동
  4. 탱크 시즈모드 개발
  5. 공격 준비 (마린 스팀팩, 탱크 시즈모드, 레이스 클로킹)
  6. 전군 1시 방향으로 공격
  7. 전군 피해
  8. 게임 종료

 

소스코드 전체를 실행하여 게임 진행 과정을 하나씩 살펴보겠습니다.

 

먼저 게임을 시작하구요.

[알림] 새로운 게임을 시작합니다.

 

종류별로 유닛이 잘 생성되었네요.

마린 유닛이 생성되었습니다.
마린 유닛이 생성되었습니다.
마린 유닛이 생성되었습니다.
탱크 유닛이 생성되었습니다.
탱크 유닛이 생성되었습니다.
레이스 유닛이 생성되었습니다.

 

전군 이동 명령을 통해 모든 유닛들이 1시로 이동합니다.

마린 : 1시 방향으로 이동합니다. [속도 1]
마린 : 1시 방향으로 이동합니다. [속도 1]
마린 : 1시 방향으로 이동합니다. [속도 1]
탱크 : 1시 방향으로 이동합니다. [속도 1]
탱크 : 1시 방향으로 이동합니다. [속도 1]
레이스 : 1시 방향으로 날아갑니다. [속도 5]

 

이동하는 중에 탱크의 시즈모드가 개발되었네요.

[알림] 탱크 시즈 모드 개발이 완료되었습니다.

 

적군 진영 바로 앞에서 공격 준비를 합니다. 각 유닛별로 특수 기술을 사용하네요.

마린 : 스팀팩을 사용합니다. (HP 10 감소)
마린 : 스팀팩을 사용합니다. (HP 10 감소)
마린 : 스팀팩을 사용합니다. (HP 10 감소)
탱크 : 시즈모드로 전환합니다.
탱크 : 시즈모드를 해제합니다.
레이스 : 클로킹 모드 설정합니다.

 

이제 전쟁 준비가 되었으므로 전면전을 개시합니다.

마린 : 1시 방향으로 적군을 공격 합니다. [공격력 5]
마린 : 1시 방향으로 적군을 공격 합니다. [공격력 5]
마린 : 1시 방향으로 적군을 공격 합니다. [공격력 5]
탱크 : 1시 방향으로 적군을 공격 합니다. [공격력 35.0]
탱크 : 1시 방향으로 적군을 공격 합니다. [공격력 35.0]
레이스 : 1시 방향으로 적군을 공격 합니다. [공격력 20]

 

전쟁 과정에서 우리 유닛들도 피해를 많이 입네요.

마린 : 7 데미지를 입었습니다.
마린 : 현재 체력은 23 입니다.
마린 : 14 데미지를 입었습니다.
마린 : 현재 체력은 16 입니다.
마린 : 15 데미지를 입었습니다.
마린 : 현재 체력은 15 입니다.
탱크 : 20 데미지를 입었습니다.
탱크 : 현재 체력은 130 입니다.
탱크 : 7 데미지를 입었습니다.
탱크 : 현재 체력은 123 입니다.
레이스 : 13 데미지를 입었습니다.
레이스 : 현재 체력은 67 입니다.

 

생각보다 피해가 크고 적군이 강력하여 아쉽지만 패배를 인정하고 gg 를 선언합니다.

Player : gg

 

게임에서 퇴장함으로써 게임을 종료합니다.

[Player] 님이 게임에서 퇴장하셨습니다.

 

 


 

스타크래프트 프로젝트에 사용된 모든 클래스들의 상관관계를 살펴보면, 가장 기본이 되는 일반 유닛인 Unit 클래스를 공격 유닛인 AttackUnit 클래스가 상속받습니다. 그리고 AttackUnit 클래스를 상속받아서 지상 공격 유닛인 마린과 탱크를 위한 Marine, Tank 클래스를 정의합니다.

공중 유닛을 위해 날아다니는 기능을 제공하는 Flyable 클래스를 두고 공중 공격 유닛인 FlyableAttackUnit 은 Flyable 클래스와 AttackUnit 클래스를 다중 상속 받고, 이 FlyableAttackUnit 클래스를 상속받아서 레이스 유닛을 위한 Wraith 클래스를 정의합니다.

회색으로 표시된 최하위에 위치하고 있는 Marine, Tank, Wraith 클래스들은 각 유닛이 가지는 서로 다른 특수 기술을 메소드로 정의하며 공격, 이동, 피해 등 공통적으로 처리되는 동작은 상속 관계에 의해 부모 클래스에 정의된 것을 그대로 사용합니다.

그리고 공중 공격 유닛은 지상을 통해 이동하지 않고 공중으로 날아서 이동하므로 Unit 클래스의 move() 메소드를 오버라이딩하여 Flyable 클래스의 fly() 메소드를 호출하도록 재정의합니다. 이를 통해 모든 유닛은 move() 메소드를 통해 지상 / 공중 구분 없이 모두 이동처리가 가능합니다.

 

 


스타크래프트 프로젝트 전체 코드입니다.

from random import *

# 일반 유닛
class Unit:
    def __init__(self, name, hp, speed):
        self.name = name
        self.hp = hp
        self.speed = speed
        print("{0} 유닛이 생성되었습니다.".format(name))

    def move(self, location):
        print("{0} : {1} 방향으로 이동합니다. [속도 {2}]"\
            .format(self.name, location, self.speed))

    def damaged(self, damage):
        print("{0} : {1} 데미지를 입었습니다.".format(self.name, damage))
        self.hp -= damage
        print("{0} : 현재 체력은 {1} 입니다.".format(self.name, self.hp))
        if self.hp <= 0:
            print("{0} : 파괴되었습니다.".format(self.name))

# 공격 유닛
class AttackUnit(Unit):
    def __init__(self, name, hp, speed, damage):
        Unit.__init__(self, name, hp, speed)
        self.damage = damage

    def attack(self, location):
        print("{0} : {1} 방향으로 적군을 공격 합니다. [공격력 {2}]" \
            .format(self.name, location, self.damage))

# 마린
class Marine(AttackUnit):
    def __init__(self):
        AttackUnit.__init__(self, "마린", 40, 1, 5) # 이름, 체력, 이동속도, 공격력

    # 스팀팩 : 일정 시간 동안 이동 및 공격 속도를 증가, 체력 10 감소
    def stimpack(self):
        if self.hp > 10:
            self.hp -= 10
            print("{0} : 스팀팩을 사용합니다. (HP 10 감소)".format(self.name))
        else:
            print("{0} : 체력이 부족하여 스팀팩을 사용하지 않습니다".format(self.name))

# 탱크
class Tank(AttackUnit):
    # 시즈모드 : 탱크를 지상에 고정시켜, 더 높은 파워로 공격 가능. 이동 불가.
    siege_developed = False # 시즈모드 개발여부 (클래스 변수)

    def __init__(self):
        AttackUnit.__init__(self, "탱크", 150, 1, 35) # 이름, 체력, 이동속도, 공격력
        self.siege_mode = False # 시즈모드 (해제 상태)
    
    # 시즈모드
    def set_siege_mode(self):
        if Tank.siege_developed == False: # 시즈모드가 개발되지 않은 경우 메소드 탈출
            return

        # 현재 시즈모드가 아닐 때
        if self.siege_mode == False:
            print("{0} : 시즈모드로 전환합니다.".format(self.name))
            self.damage *= 2 # 공격력 2배로 증가
            self.siege_mode = True # 시즈 모드 설정
        # 현재 시즈모드일 때
        else:
            print("{0} : 시즈모드를 해제합니다.".format(self.name))
            self.damage /= 2 # 공격력 절반으로 감소
            self.siege_mode = False # 시즈 모드 해제

# 날 수 있는 기능을 가진 클래스
class Flyable:
    def __init__(self, flying_speed):
        self.flying_speed = flying_speed

    def fly(self, name, location):
        print("{0} : {1} 방향으로 날아갑니다. [속도 {2}]"\
            .format(name, location, self.flying_speed))

# 공중 공격 유닛
class FlyableAttackUnit(AttackUnit, Flyable):
    def __init__(self, name, hp, damage, flying_speed):
        AttackUnit.__init__(self, name, hp, 0, damage)
        Flyable.__init__(self, flying_speed)

    def move(self, location):
        self.fly(self.name, location)

# 레이스
class Wraith(FlyableAttackUnit):
    def __init__(self):
        FlyableAttackUnit.__init__(self, "레이스", 80, 20, 5) # 체력, 공격력, 공중 이동 속도
        self.cloaked = False # 클로킹 모드 (해제 상태)

    # 클로킹 모드
    def cloaking(self):
        # 현재 클로킹 모드일 때
        if self.cloaked == True:
            print("{0} : 클로킹 모드 해제합니다.".format(self.name))
            self.cloaked = False
        # 현재 클로킹 모드가 아닐 때
        else:
            print("{0} : 클로킹 모드 설정합니다.".format(self.name))
            self.cloaked = True

# 게임 시작
def game_start():
    print("[알림] 새로운 게임을 시작합니다.")

# 게임 종료
def game_over():
    print("Player : gg") # good game
    print("[Player] 님이 게임에서 퇴장하셨습니다.")

# 실제 게임 진행
game_start()

# 마린 3기 생성
m1 = Marine()
m2 = Marine()
m3 = Marine()

# 탱크 2기 생성
t1 = Tank()
t2 = Tank()

# 레이스 1기 생성
w1 = Wraith()

# 유닛 일괄 관리 (생성된 모든 유닛 append)
attack_units = []
attack_units.append(m1)
attack_units.append(m2)
attack_units.append(m3)
attack_units.append(t1)
attack_units.append(t1)
attack_units.append(w1)

# 전군 이동
for unit in attack_units:
    unit.move("1시")

# 탱크 시즈모드 개발
Tank.siege_developed = True
print("[알림] 탱크 시즈 모드 개발이 완료되었습니다.")

# 공격 모드 준비 (마린 : 스팀팩, 탱크 : 시즈모드, 레이스 : 클로킹)
for unit in attack_units:
    if isinstance(unit, Marine): # Marine 의 인스턴스이면 스팀팩
        unit.stimpack()
    elif isinstance(unit, Tank): # Tank 의 인스턴스이면 시즈모드
        unit.set_siege_mode()
    elif isinstance(unit, Wraith): # Wraith 의 인스턴스이면 클로킹
        unit.cloaking()

# 전군 공격
for unit in attack_units:
    unit.attack("1시")

# 전군 피해
for unit in attack_units:
    unit.damaged(randint(5, 20)) # 공격은 랜덤으로 받음 (5 ~ 20)

# 게임 종료
game_over()
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함