'Coding'에 해당되는 글 3건

  1. 2013.04.05 Duck Typing에 대하여. (2)
  2. 2010.04.13 체력 시스템에 대한 뭔가 잡다한 생각 (4)
  3. 2010.04.05 하프라이프2 모드 코딩 삽질. (3)

오리처럼 울고 오리처럼 걷고 오리의 깃털을 가진 축생은 오리라 할 수 있을까요?

카테고리가 코딩인데 뭔 뜬금없는 오리 이야기냐 하시겠지만, 일단 파이선에서는 그렇다고 합니다.

# coding=UTF-8[각주:1]

class Duck:
  def Quack(self):
    print "꽤애애애애애액!"
  def Feathers(self):
    print "이 오리는 녹색 깃털로 뒤덮여 있습니다."

class Person:
  def Quack(self):
    print "이 사람은 최대한 애처롭게 꽤애액 거립니다."
  def Feathers(self):
    print "이 사람은 임기응변으로 땅에 떨어진 오리의 깃털을 줍습니다."

donald = Duck()
jack = Person()

print "- 도날드의 경우 -"
donald.Quack()
donald.Feathers()
print ""
print "- 잭의 경우 -"
jack.Quack()
jack.Feathers()

이 코드를 실제로 돌려보면 이런 결과가 나옵니다.

1
2
3
4
5
6
7

- 도날드의 경우 - 꽤애애애애애액! 이 오리는 녹색 깃털로 뒤덮여 있습니다. - 잭의 경우 - 이 사람은 최대한 애처롭게 꽤애액 거립니다. 이 사람은 임기응변으로 땅에 떨어진 오리의 깃털을 줍습니다.

앞에서 "오리처럼 울고 오리처럼 걷고 오리의 깃털을 가진 축생"의 이야기를 꺼냈는데, 그러면 이 축생과 이 예의 공통점은 무엇일까요? 그렇습니다, 뭔가 미심쩍지만 Duck에게서 코더가 예상하고 있는 "Duck처럼 Quack할 수 있고 Duck의 Feathers를 갖고 있는" 특징을 그대로 가지고 있으니 Person도 Duck이라고 치는 것이지요. 이것을 보고 Duck typing이라고 합니다. 오리의 예를 들어서 성립된 개념이니까 Duck typing이라는 이름이 붙은 것이지요.[각주:2]

Duck typing이라는 용어가 생겨난 것 자체는 2000년으로 거슬러 올라간다고 합니다.[각주:3] 파이선 뉴스그룹에 이런 글이 올라온 것이 시초였죠.

그러니까, 그게 오리인지 검사하지 말고, 당신에게 오리의 무슨 행동이 필요한지에 따라 오리처럼 우는지, 오리처럼 걷는지, 등등, 등등 적절한 행동을 오리처럼 하는지 검사하세요.[각주:4]

- Alex Martelli, 2000

파이선은 인터페이스 같은 삽질을 하지 않고도[각주:5] 단지 클래스에서 적절한 함수의 이름을 불러주는 것만으로도 인터페이스 비스무리한 짓거리 내지는 인터페이스가 구현하고자 하는 바를 이룰 수 있습니다! 오오 파이선!

......이면 지금 이 포스팅의 제목이 파이선 강의가 되었어야겠죠.

저 Duck typing이라는 개념은 꽤 논란이 있는 개념입니다. 뭐 일단 앞에서 주구장창 말씀드렸다시피 다중상속이니 추상클래스 구현이니 같은 장대한 삽질을 하지 않고도 비슷한 일을 하는 함수가 이름이 같으면 코드를 그대로 컨트럴 씨브이를 시전해도 잘 돌아갑니다. 예를 들어 오리 클래스를 만들었으니 이번에는 오리랑 비슷하게 꽥꽥거리는 거위 클래스를 만들고 싶겠죠. 분명 이것도 꽥꽥거리고 깃털이 있지만 오리는 아니니 오리에서 상속시키기도 난감하고, 그래서 그냥 새로 하나 만들기로 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# coding=UTF-8

class Duck:
  def Quack(self):
    print "꽤애애애애애액!"
  def Feathers(self):
    print "이 오리는 녹색 깃털로 뒤덮여 있습니다."

class Swan:
  def Quack(self):
    print "꽤애애애애애액!"
  def Feathers(self):
    print "이 거위는 매우 아름다운 흰 털에 싸여 있습니다."

이 거위(class Swan)도 물론 아까와 마찬가지로 울부짖을(?) 수 있습니다. 또, 갑자기 도날드의 종을 Swan으로 바꿔야 할 때도 다른 부분은 냅두고 donald = Duck() 부분만 donald = Swan()으로 바꿔준다면 쉽게 유지보수가 가능합니다.

이렇게 보면 기존 코드 재활용에도 좋고, 간편한게 덕 타이핑인 것 같습니다만, 문제는 오리처럼 울고 오리의 깃털을 가진 게 용일 때 발생합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# coding=UTF-8

class Dragon:
  def Quack(self):
    print "키에에에에에엥!"
    raise Exception("용이 빡쳐서 화염을 내뿜었습니다.")
  def Feathers(self):
    print "이 용은 오리의 그것과 닮은 녹회색 깃털을 달고 있습니다."

donald = Dragon()

print "- 용 도날드의 경우 -"
donald.Quack()
donald.Feathers()

보시다시피, Dragon은 분명 울기도 하고 깃털도 있지만, 이 놈의 울음소리는 예사 울음소리가 아니라 돌려보는 것 자체가 Exception이 됩니다(...). 이걸 돌려보면 아래와 같은 결과가 나타납니다.

1
2
3
4
5
6
7
8
- 용 도날드의 경우 -
키에에에에에엥!
Traceback (most recent call last):
  Line 13, in <module>
    donald.Quack()
  Line 6, in Quack
    raise Exception("용이 빡쳐서 화염을 내뿜었습니다.")
Exception: 용이 빡쳐서 화염을 내뿜었습니다.

깃털을 볼 새도 없이 용이 화염을 내뿜어서 프로그램이 조기종료되어버렸습니다! 이처럼 클래스가 Quack이란 메서드를 가지고 있긴 한데 그 Quack이 우리가 생각하는 그 Quack이 아니라면 저런 식으로 얼마든지 문제를 일으킬 소지가 있는 것입니다. 물론 Dragon에 대해 첨부된 문서를 보고 거기서 Quack에 대한 설명을 주의깊게 읽었다면 저런 문제를 일으킬 리가 없기는 합니다.

덕 타이핑 자체는 꽤나 편리한 개념이라 정적 타입 언어인 C#과 .NET 프레임워크에까지 도입되는 기염을 토하게 됩니다. dynamic이라는 키워드가 그것인데요, 예를 들어 dynamic q;를 선언해두고 q.Quack()을 호출하면 q라는 클래스에 인자 없는 Quack이라는 함수가 있다면 그걸 그대로 부르게 됩니다.

하지만 위에서 보여준 여러 가지 문제점들 때문에 저는 개인적으로 덕 타이핑을 쓰지 않습니다. 일단 덕 타이핑의 강점은 복붙이 편리하다는 점인데, 저렇게 문서 다 체크하고 앉아있으면 그 편의성도 결국에는 날려먹게 되죠. 또한, 함수를 호출하기 전에 최소한 그 함수가 속해 있는 클래스가 무엇인지 기본적으로는 알아야 한다는 것이 제가 프로그램을 짤 때 기본적인 철칙입니다. 오리의 비유를 다시 들자면, 그게 오리처럼 날아다니건 깃털이 오리같이 생겼건 오리과에 속해 있지 않으면 오리고 뭐고  무엇보다 결정적으로 제가 주로 쓰는 C#에는 dynamic 같은 짓 안 해도 인터페이스추상 클래스라는 신의 발명품이 있습니다.

interface IAnatidae[각주:6]
{
    public void Quack();
    public string Feathers { get; }
}

class Duck : IAnatidae
{
    public void Quack()
    {
        Console.WriteLine("꽤애애애애애액!");
    }

    public string Feathers
    {
        get { return "녹색 깃털"; }
    }
}

여기서 오리가 될지 뭐가 될지는 모르겠지만 오리과에 속하는 뭔가를 풀고 싶으면 IAnatidae의 변수를 하나 선언해주면 됩니다. 뭔진 모르겠지만 그건 IAnatidae의 Quack 메서드와 Feathers 속성을 그대로 가지고 있을 거거든요. 그리고 그걸 울게 만들고 싶다면 그 변수에서 Quack 메서드 한 방 때려주면 됩니다. 복붙도 쉽습니다. 상대가 같은 IAnatidae면 선언할 때 생성자 하나 바꿔주면 땡입니다. 물론 누군가 악의적으로 Dragon을 IAnatidae로 속여서 그걸로 문제를 일으키는 것까지 막지는 못합니다. 하지만 최소한 "오리처럼 운"다고 해서 용이 오리가 되지는 않습니다.

두 번째 문제는 역시 성능입니다. 덕 타이핑이 개입된다는 얘기는, 메서드나 속성의 이름을 가지고 함수를 찾아야 한다는 얘기입니다. 그러니까, 클래스를 본다→이름 표에서 "Quack"을 찾는다→Quack들 중에 인자를 받지 않으며 메서드인 것을 찾는다→찾아낸 메서드를 호출한다 라는 과정을 거쳐야 한다는 얘깁니다. 반면에 앞에서 말씀드린 인터페이스를 사용하는 언어에서는 클래스를 본다→IAnatidae 인터페이스에 정의된 Quack이 어디에 구현되어 있는지 주소표를 본다→구현된 메서드를 호출한다 라는 과정으로 이루어집니다. 앞에 비하면 빠르겠죠. 물론 그 성능차이를 씹어먹을 정도로 덕 타이핑이 편하면 모를까, 저는 앞의 이유로 덕 타이핑이 그 정도로 편하다고 생각하지는 않습니다.

이런 이유로 저는 덕 타이핑이란 개념 자체는 나쁘지 않다고 생각합니다만, 그것을 제가 실제로 사용하지는 않습니다. 여러분의 생각은 어떠신가요?

  1. 원래의 영어로 된 예제에서는 이 놈이 필요 없었습니다. 그러나 본 블로그의 주 언어는 한국어(...)이므로 설명을 위해 print 문에 한글을 쓰다보니 저 주석이 필요해지더군요. [본문으로]
  2. 저 "오리처럼 울고 오리처럼 걷는 축생"의 이야기는 사실 프로그래밍이라는 개념이 생기기 한참 전에 나온 "오리 테스트"라는 개념입니다. 귀납 논증을 까거나(오리처럼 걷는다고 다 오리냐?) 쉽게 이해시킬 때(오리처럼 걷고 오리처럼 우니까 새로운 오리의 종이 틀림없어) 사용되는 개념이죠. [본문으로]
  3. 출처 http://en.wikipedia.org/wiki/Duck_typing [본문으로]
  4. In other words, don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need to play your language-games with. [본문으로]
  5. ......태클 거실 분들을 위해, 물론 파이선에도 인터페이스와 추상 클래스라는 개념은 존재합니다. 다만 인터페이스가 Duck typing보다 꽤나 늦게 도입된 편입니다. [본문으로]
  6. I오리과(......) [본문으로]
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 애쉬군
플레이어가 데미지를 입었을 때 체력을 책정하는 방식으로는 여러가지가 있을 수 있다.

1. 하프2 코드 딸려온 그대로 냅둔다. 즉, 수치화되었고 자동 회복/자동 데미지따위 없는 체력 시스템.

2. 콜옵에서 영감을 얻어온 방식.
데미지를 입은지 일정 시간이 지나면 다시 체력이 100%로 회복된다.
아니면 한번에 입은 데미지가 적을수록 회복되는데 걸리는 시간이 줄어든다든가 하는 것도 괜찮음.
장점: 굳이 메디킷을 떨궈서 밸런싱을 할 필요가 없다. 엄폐 잘 되면 장땡.
단점: 총에 엄청난 오버파워가 부가되어서 이쪽으로 밸런싱 잘못하면 망판이 될 수 있음.
용도: 빠른 페이스 게임에 적합
FPS 실례: 당연히 콜옵시리즈(1제외) -_-+

3. 재기드 얼라이언스2의 오리지널 방식.
기본적으로 체력관련 스탯이 체력과 부상으로 나뉘어 있다.
이 때 부상이 너무 높으면 초당 데미지를 입기 시작한다.
'응급치료 키트'를 사용하면 '부상'은 회복하지만 '체력'은 회복하지 못한다.
예를 들어서, 풀체 100에 부상 0이라고 하자.
데미지가 30인 총알을 한 대 맞으면 체력 70이 되고 부상 30이 생긴다.
이 상태에서 계속 냅두면 체력이 떨어지고 동시에 그만큼 부상이 생긴다.
다시 총알맞은 시점으로 돌아와서, 만약 여기서 응치를 하면 체력은 여전히 70이지만 부상은 0으로 됨.
But, 여기서 예의 30짜리 총알을 같이 맞으면 체력은 40이 되고 부상은 더 생겨서 한 40 정도가 터진다.
부가적으로 부상을 당하거나 체력이 낮으면 사격하는데 큰 어려움이 있는 정도의 상태이상이 생겨서 철수를 해서 응치를 받거나 아예 의료 시술을 받아야 하는(=전장인 경우 후송되어야 하는) 정도가 된다.
장점: 플레이어가 리스폰이나 몸빵을 믿고 함부로 앞에 나가는 일이 없게 된다.
단점: 부상 입은 플레이어가 체력을 회복할 수단이 없어서 빠른 리스폰이 있거나 후송받아서 치료를 금방 받을 수 있지 않는 한 게임이 어려워진다.
그런데 애초에 이 시스템은 리얼리티를 위한 것인데, 빠른 리스폰이나 빠른 후송후 치료가 있다면 리얼리티가 팍 깎여서 애초에 이 시스템을 도입하는 의미가 없음.
용도: 게임이 전면전보다는 전술전에 가까울 때.
FPS 실례: 아직 본 적 없음.

4. 3에서 영감을 받은 한 가지 방식.
물론 부상 시스템이 리얼리티를 높이는 데는 좋지만, 후송이라는 절차가 따로 필요하다든지, 공격팀이 순식간에 우세를 잡아버리는 등의 문제가 있을 수 있다.
그래서 '부상'이라는 스탯에 대해서 다시 생각을 해 보면, 부상이라는 것은 그만큼 몸이 상했다는 증거긴 하지만, 부상을 크게 당했다고 당장 행동이 멎어버리는 것은 아니지 않나?
그러니까 이렇게 하는 거다. 여기서 다시 풀체 100인 전사의 예를 들도록 하자. 그러니까 외상(外狀) 100/내상 100이다.
여기서 데미지 30짜리 총알을 맞는다. 그런데 부상률이 30%라서 앞뎀은 30이지만 부상은 39. 그러므로 플레이어가 보기에는 외상 70의 체력이 보이지만 실제로는 내상 61이 현재 체력.
70에서 점점 체력이 떨어지기 시작해서 마침내는 61에 도달하게 된다.
단, 외상 70일 때 재빨리 체력 20을 회복시키는 조치를 받는다면(메드키트가 될 수도 있고, 아니면 응치가 될 수도 있고) 외상과 내상 모두 90으로 굳어진다. 81이 아니다.
데미지의 특성상 외상이 오히려 내상보다 피해가 클 때도 있다. 예를 들어, 팀포2의 스카웃의 야구공 러시를 생각해 보자. 현실에서 그렇게 맞는다면 외상으로는 순간적인 전투불능을 가져오지만, 내상은 정작 10정도의 피해밖에 없다. 즉 상태 0/90. 이 때도 마찬가지로 0에서 점점 외상이 올라가기 시작해서 특별히 데미지를 입지 않는다면 90으로 올라가게 된다.
물론 이 때 치료를 받으면 110/110(오버힐이란 개념이 있다면)이 된다.
외상이 낮으면 이동속도와 명중률에서 페널티를 받음.
장점: 몸빵러시를 할 수 있다는 것과 언젠가는 뒤로 빠져야 한다는 개념이 적당히 조합되어 있다.
단점: 시스템이 쓸데없이 복잡해진다. 또 제멋대로인 기준을 제대로 바로잡지 못하면 제일 최악의 밸런스가 나온다.
용도: 조금 느린 페이스의 게임. 빠른 페이스 게임에 도입해도 상관은 없다.
FPS 실례: 내외상 불일치의 개념은 레포데 시리즈에서 진통제를 먹었을 때 체험해 볼 수 있다. 또 "외상이 내상보다 피해가 크면 서서히 회복된다"는 개념은 이미 하프라이프 시리즈에서 물속에 들어갔다가 나와 보면 잘 알 수 있음.

5. 마비노기식.
데미지를 입으면 꽤 큰 양이 체력에서 빠지고, 조금씩 조금씩 부상을 입기 시작한다.
체력이 너무 많이 빠진 것 같아서 뒤로 빠져서 회복을 해도 부상 때문에 일정 체력 이상으로는 회복하지 못한다.
장/단점: 솔직히 파악을 못 하겠다.
용도: 게임이 RPG로 흘러갈 때?
FPS 실례: 본 적 없음.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 애쉬군

대충 이런 삽질을 했다.
...마귀같은 HL2Dm......

대체 콜옵같은 체력시스템 만드는 거 하나가 왜이리 어렵냐.

"저게 뭐가 달라졌다는 건데?" 하고 따지려는 분들을 위한 친절한 설명:
1. 왼쪽 아래의 체력HUD 변화에 주목하자(솔까말 진짜 콜옵같이 할라면 저것부터 없애야 하는데 말이지)
2. 동영상 중간에 보면 어찌어찌 편법으로 나만 357 매그넘 들고(bot_mimic 1이었음) 상대를 쏘는 장면이 나온다.
맞아봤으면 알겠지만 데스매치에서 357 매그넘 한 방의 데미지는 85.
그리고 몇 초 뒤, 다시 쐈는데 콤바인이 살아있다.

......다음에 할 것들을 생각하니까 눈물이 앞을 가립니다.
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 애쉬군


티스토리 툴바