2009. 6. 3.

HTML5 의 Web 게임 가능성..

Google Chrome 이후로 자바스크립트에 많은 관심이 간다.
그중에 하나가 브라우저 상에서의 애니메이션을 제어하는 기술이다.
HTML 5 상에서의 CANVAS로 수행하는 상당히 많은 예제를 보고 있자면 실제로 사용이 가능한 경우가 상당히 많을 것으로 생각한다.
오늘자 Slashdot에서는 플래시 없이 동영상을 VIDEO태그로 실행하는 사이트가 올라왔다. Firefox 3.5 에서만 볼 수 있는 것이 아쉽지만, 앞으로 HTML 5 가 플러그인 시장에 얼마나 엄청난 타격을 입힐지는 기대가 된다.

말하자면 컴퓨터쪽에서의 강력한 연산기능대신 비디오/오디오 등에 특화된 CPU가 있다면, 서버쪽에서 대부분의 연산을 담당하고 클라이언트에서는 웹브라우저가 실제 플랫폼을 대신하는 상황도 생각할 수 있다.

Erlang , LISP으로 만든 게임에서 플레이어 DB데이터와 게임액션을 Erlang이 담당하고, 게임상의 상태처리, 길찾기, API등을 Lisp으로 처리하는 방식은 상당히 좋은 Role Model이 될 수 있을듯하다.

ARM등의 비디오/오디오 강화 CPU에 실연산처리는 클라우드를 포함하는 대규모 서버군이 처리하고, 광대역 처리만 제대로 된다면 기존의 플러그인없이도 HTML 5 만으로도 훌륭한 게임이 나올 수 있을 듯하다.



관련링크


2009. 6. 1.

LISP으로 어드벤처 게임을... Ch 12

이제 각 방별로 해당 방에 들어갔을때 설명을 출력하도록 한다.

;;;; 게임의 Castle 구성하기..
;;; north south east west up down mobs
(defvar *castle*
  '((0 2 0 0 0 0 0 ("You are in the hall way." "There is a door in south." "Through window to the north you can see secret herb garden"))
    (1 3 3 3 0 0 0 ("This is the audience chamber" "There is a window to the south. By looking to the right" "Through it you can see the entrance of the castle" "Doors leave this room to the north, east, and south" ))
    (2 0 5 2 0 0 0 ("You are in great hall, all-shaped room" "There is doors to the east and to the north" "In the alcove is a door to the west"))
    (0 5 0 0 0 0 0 ("This is the monarch's private meeting room" "There is a single exit to the south"))
    (4 0 0 3 15 13 0 ("This inner hallway contains a door to the north." "And one to the west, and a circular stairwell" "passes though the room" "You can see ornamental lake through here"))
    (0 0 1 0 0 0 0 ("You are at the entrance to a fobidding-looking" "stone castle. You are facing east"))
    (0 8 0 0 0 0 0 ("This is castle's kitchen. Though windows in" "The north wall you can see secret herb garden." "A door leave the kitchen to the south" ))
    (7 10 0 0 0 0 0 ("You are in store room admist spices" "vegatables, and vast sacks of floud and" "Other provisions. There is a door to the north" "and one to the south"))
    (0 19 0 8 0 8 0 ("You are slowly descends."))
    (8 0 11 0 0 0 0 ("You are in the real vestibule." "There are windows to the south from which" "you can see ornamental lake" "There is an exit to the east and " "one to north"))
    (0 0 10 0 0 0 0 () )
    (0 0 0 13 0 0 0 ("you are in dank dark dungeon" "There is single exit, a small hole in" "wall towards to east"))
    (0 0 12 0 5 0 0 ("You are in the prison guardroom in the " "basement of the castle. The staiwell" "ends in the room. There is one other" "exit, a small hole in the east wall") )
    (0 15 17 0 0 0 0 ("You are in the master bedroom on the upper" "level of the castle..." "Looking down from the window to the west, you" "can see the entrance to the castle, while the" "secret herb garden is visible below to the north" "window. There are doors to the east and" "to the south"))
    (14 0 0 0 0 5 0 ("This is L-shaped upper hallway." "To the north is a door, and there is a strairwell in the hall as well. You can see" "the lake through the south windows."))
    (17 0 19 0 0 0 0 ("This room was used as the castle treasury in" "by-gone years..." "There are no windows, just exits to the" "north and to the east"))
    (18 16 0 14 0 0 0 ("Ooooh.. you are in the chembermaids' bedroom" "There is an exit to the west and a door " "TO the south"))
    (0 17 0 0 0 0 0 ("This tiny room on the upper level is the" "dressing chamber. There is a window to the" "north. with a view of the here garden down" "below. A door leaves to the south."))
    (9 0 16 0 0 0 0 ("This is a small room outside the castle" "lift which can be entered by a door to the north" "another door leads to the west, you can see" "the lake through the southern windows"))))

설명을 출력할 부분을 만든다.

먼저 방에서 각 요소를 가져오는 함수에, 새로 설명을 출력할 부분을 추가한다.

;;; 방의 리스트에서 각 요소를 가져오는 함수
(defun get-rooms-element (room element)
  (cond ((char= element #\n) (car room))
        ((char= element #\s) (nth 1 room))
        ((char= element #\e) (nth 2 room))
        ((char= element #\w) (nth 3 room))
        ((char= element #\u) (nth 4 room))
        ((char= element #\d) (nth 5 room))
        ((char= element #\m) (nth 6 room))
        ((char= element #\v) (nth 7 room))
        (t
         nil)))

다음으로 해당하는 방의 설명을 출력하는 함수를 만든다.

;;; 방의 모습을 설명하는 루틴
(defun describe-room ()
  (format t "The room is ~A~%" *room*)
  (print-room *room*)
  (if (= *room* 9) (setf *room* 10)))

9번방에 들어온 경우에는 자동으로 10번방으로 이동한다.

;;; 방의 내역을 출력하기
(defun print-room (which-room)
  (let ((desc-list (get-rooms-element (get-room which-room ) #\v)))
    (labels ((iter (field)
               (cond ((null field) )
                     (t
                      (format t "~a~%" (car field))
                      (iter (cdr field))))))
      (iter desc-list))))

마지막으로 플레이어가 11번방으로 들어왔다면 게임을 종료한다.

;;; 전체 이벤트 루프

(defun event-loop ()
  (let ((k (get-rooms-element (get-room *room*) #\m))
        (game-over nil))
    (loop
       (status-report)
       (cond ((= *room* 11)
              (progn
                (format t "Congratulation, you escape the castle")
                (setf game-over t)))
             (t
              (let ((input-key (keyboard-event)))
                (cond ((string= input-key "n") (go-direction #\n))
                      ((string= input-key "s") (go-direction #\s))
                      ((string= input-key "e") (go-direction #\e))
                      ((string= input-key "w") (go-direction #\w))
                      ((string= input-key "u") (go-direction #\u))
                      ((string= input-key "d") (go-direction #\d))
                      ((string= input-key "c") (consume-food))
                      ((string= input-key "f") (fight))
                      ((string= input-key "r") (runaway))
                      ((string= input-key "q") (setf game-over t))
                      ((string= input-key "m") (magic-amulet))
                      ((string= input-key "p") (picking-up))
                      ((string= input-key "i") (inventory))))))
       (if (eq game-over t)
           (return-from event-loop)))))

다음 부터는 완성된 버전을 LISP식으로 조금씩 변화시켜나가보도록 하겠다.

2009. 5. 28.

LISP 으로 어드벤처 게임을... Ch 11

전투 모듈에서는 몹에 대한 공격과 방어가 확률에 의해 진행된다.
방어구를 입은 경우 넘겨야할 난이도를 75%정도로 낮추어서 게임 진행을 쉽게 한다.

;;; 전투모듈
(defun fight ()
  (prompt-read "press return to fight")
  (cond ((= *suit* 1)
         (progn
           (format t "Your armor increases your chance of success~%")
           (setf *ff* (* 3 (floor (/ *ff* 4)))))))
  (cond ((and (= *axe* 0) (= *sword* 0))
         (progn
           (format t "You have no weapons~%You mush fight with Base hands~%")
           (setf *ff* (+ *ff* (floor (/ *ff* 5))))))
        ((and (= *axe* 1) (= *sword* 0))
         (progn
           (format t "You have only an axe to fight with~%")
           (setf *ff* (* 4 (floor (/ *ff* 5))))))
        ((and (= *sword* 1) (= *axe* 0))
         (progn
           (format t "You mush fight with your sowrd~%")
           (setf *ff* (* 3 (floor (/ *ff* 4))))))
        (t
         (let ((z (parse-integer (prompt-read "Which weapon? 1- Axe, 2- Sword"))))
           (cond ((= z 1) (setf *ff* (* 4 (floor (/ *ff* 5)))))
                 ((= z 2) (setf *ff* (* 3 (floor (/ *ff* 4)))))))))
  (taking-arms))
실제 전투모드에서는 확률에 의거해 공방을 결정하고, 전투가 끝나는 것도 확률로 결정한다.
전투종료시 난이도 값을 얼마나 남겼는지를 확인해서 승패를 결정한다.

;; 배틀모드
(defun taking-arms ()
  (let ((prob1 (random 10))
        (prob2 (random 10))
        (prob3 (random 10))
        (prob4 (random 100))
        (prob5 (random 10)))
    (cond ((> prob1 5)
           (format t "~a attacks~%" *monster*))
          (t
           (format t "you attack~%")))
    (cond ((> prob2 5)
           (progn
             (format t "You manage to wound it~%")
             (setf *ff* (floor (* 5 (/ *ff* 6)))))))
    (cond ((> prob3 5)
           (progn
             (format t "The monster wounds you!~%")
             (decf *strength* 5))))
    (cond ((> prob4 35)
           (taking-arms))
          (t
           (cond ((> (* prob5 1.6) *ff*)
                  (progn
                    (format t "~%and you managed  to kill the ~a" *monster*)
                    (incf *mk*)
                    (setf (nth 6 (nth (- *room* 1) *castle*)) 0)))
                 (t
                  (progn
                    (format t "The ~a defeated you." *monster*)
                    (setf *strength* (floor (*strength* 2))))))))))

2009. 5. 27.

LISP 으로 어드벤처 게임을 ... CH 10

여기서 만들게 될 함수들은 상점, 음식먹기 등에 해당한다.
먼저 지난번에 만들었던 Event Loop를 살짝 수정했다.

(defun event-loop ()
  (let ((k (get-rooms-element (get-room *room*) #\m))
        (game-over nil))
    (loop
       (status-report)
       (let ((input-key (keyboard-event)))
         (cond ((string= input-key "n") (go-direction #\n))
               ((string= input-key "s") (go-direction #\s))
               ((string= input-key "e") (go-direction #\e))
               ((string= input-key "w") (go-direction #\w))
               ((string= input-key "u") (go-direction #\u))
               ((string= input-key "d") (go-direction #\d))
               ((string= input-key "c") (consume-food))
               ((string= input-key "f") (fight))
               ((string= input-key "r") (runaway))
               ((string= input-key "q") (setf game-over t))
               ((string= input-key "m") (magic-amulet))
               ((string= input-key "p") (picking-up))
               ((string= input-key "i") (inventory))))
       (if (eq game-over t)
           (return-from event-loop)))))


마법 아뮬렛이 있을때 수행할 함수를 만든다.
;;; 마법 아뮬렛을 사용할 때 수행할 곳(6번과 11번은 제외 )
(defun magic-amulet ()
  (let ((ro (+ (random 19) 1)))
    (cond ((or (= ro 6) (= ro 11)) (magic-amulet))
          (t
           (setf *room* ro)))))


또한 음식을 먹는 함수를 만든다.

;;; 음식 먹기
(defun consume-food ()
  (cond ((>= food 1)
         (progn
           (format t "You have ~a units of food~%" *food*)
           (format t "How many do you want to eat? :")
           (let ((z (parse-integer (read-line))))
             (if (> z food)
                 (consume-food)
                 (progn
                   (decf *food* z)
                   (incf *strength* (* 5 z)))))))))


이제 방에 보물이 있는 경우 집는 함수를 만든다.
;;; 방의 물건 집기
(defun picking-up ()
  (let ((treasure (get-rooms-element
                   (get-room *room*)
                    #\m)))
    (if (< treasure 10)
        (format t "There is no treasure to pickup")
        (progn
          (incf *wealth* treasure)
          (setf (nth 6 (nth (- *room* 1) *castle*)) 0)))))

마지막으로 상점에서 물건을 구입하는 함수를 만든다.
;;; Show shop
(defun show-shop ()
  (format t "You can buy 1 - Flamming Torch ($15)~%")
  (format t "            2 - Axe ($10)~%")
  (format t "            3 - Sword ($20)~%")
  (format t "            4 - Food ($2 per unit)~%")
  (format t "            5 - Magic of Amulet ($30)~%")
  (format t "            6 - Suit of Armor ($50)~%")
  (format t "            0 - To continue Adventure ~%"))

;;; Shop에서 물건사기
(defun inventory ()
  (format t "Provision & Inventory~%")
  (cond ((> *wealth* 0)
         (progn
           (show-shop)
           (let ((z (parse-integer (prompt-read "Enter no of item:"))))
             (cond ((= z 1)
                    (progn
                      (setf *light* 1)
                      (decf *wealth* 15)))
                   ((= z 2)
                    (progn
                      (setf *axe* 1)
                      (decf *wealth* 10)))
                   ((= z 3)
                    (progn
                      (setf *sword* 1)
                      (decf *wealth* 20)))
                   ((= z 4)
                    (progn
                      (let ((q (prompt-read "how many food do you want")))
                        (cond ((> (* q 2) *wealth*)
                               (format t "You Can't Get enough money"))
                              (t
                               (progn
                                 (incf *food* q)
                                 (defc *wealth* (* q 2))))))))
                   ((= z 5)
                    (progn
                      (setf *amulet* 1)
                      (decf *wealth* 30)))
                   ((= z 6)
                    (progn
                      (setf *armor* 1)
                      (decf *wealth* 50)))))))
        (t
         (format t "You have no money~%"))))

일단 기본적인 툴이 모두 마련되었다.
다음장에서는 전투에 대한 부분을 살펴보자.

2009. 5. 25.

2D 플랫폼이 훌륭했던 이유..

What made those old, 2D platformers so great?를 대충 정리해보면..
  1. 조작
  2. 레벨 디자인
  3. 상태
등을 들 수 있다.

조작
조작은 플레이어에 부여하는 모든 능력과 속성을 의미한다. 달리기나, 점프, 스핀대시같은 다양한 이동방법부터 일시적인 무적상태같은 특이상황을 모두 아우른다.

좋 은 조작식이란 간결하고 명확해야한다. 최대한 직관적으로 설계해야하며, 사용자에게 혼동을 줄 수 있는 요소는 최대한 배제해야한다. 예를 들어, 게임중에 오브젝트와 충돌하는 경우,  아이템의 경우에는 습득이 되어야하며, 적과의 경우라면 피해를 입게해야한다. 아이템을 얻는데 필요이상의 동작이 필요하거나, 적과의 충돌을 애매하게 판정해서는 안된다.

이러한 기본 가정하에서 사용자와의 인터페이스를 구성할 수 있다.

좋은 조작법이란 현실에서 보기힘든 빠르고, 정교한 움직임을 보임으로서, 사용자의 동기를 유발할 수 있어야한다. 하지만 본질적인 흥미는 레벨디자인과의 조화에서 얻을 수 있어야한다.

레벨 디자인
게 임을 고유한 외양을 지니는 개별적인 지역으로 구성함으로써, 상당한 효과를 얻을 수 있다. 또한, 상호작용적인 요소를 배치하여, 이러한 특징을 부각시킬 수 있다. 하지만, 이러한 요소는 종종, 부조화를 일으킬 수 있기 때문에 사용에 주의를 해야한다.
사소한 문제같지만, 적절히 규제되지 않는다면 사용자가 혼동을 일으킬 수 있다.

배 경과 동시에, 게임의 레벨은 다양한 플랫폼으로 구성되어있다. 사다리, 미끄럼틀, 순간 이동장치, 계단, 입구같은 요소가 그것이다. 이런 요소는 게이머가 실제로 게임을 진행하는 지역이기때문에, 그 메커니즘을 명확하고 분명하게 이해할 수 있어야한다. 이 부분에서 조작성과의 조화가 이루어져야한다.

좋은 플레이 공간은 플레이어를 다양한 능력을 사용할 수 있도록 끌어당긴다. 기존에 존재하는 다양한 요소를 십분 이용하도록 유도하거나 어느 정도 강제할 정도가 되어야한다.

특 정 지역을 들어가거나 떠나도록 하는 요소를 분명하게 알려주는 것 역시 큰 도움이 된다. 게임상에 수집해야할 요소를 경로에 따라 배치함으로서 게이머가 길을 잃지 않도록 배려할 수 있으며, 이를 통해 게임은 더 쉽고, 즐기기에 편안하게 된다.

상태
다음의 씬을 상상해보자: 캐릭터가 막 점프를 했다. 적은 캐릭터를 노리고, 미사일을 벽에대고 쏘았고, 벽을 파괴시켰다. 이런 씬에서 몇가지 상태를 찾을 수 있다: 점프, 추적, 발사, 파괴가 그것이다.

상 태는 게임상의 물체의 행동과 속성에 붙는 일련의 논리적인 단위의 명명이라고 할 수 있다. 이를 통해 적과 보스의 핵심적인 인공지능과, 플레이어가 이를 파악하고 패턴을 깨는 행위를 표준화 할 수 있다. 게임상의 몹과 요소에 다양한 개성을 부여함으로서, 이에 대항하는데 여러가지 기법을 사용하도록 부추킬 수 있다.

또한 이러한 메커니즘을 효율적으로 배치한다면 상당한 리플레이성을 부여할 수 있으며, 타임어택같은 하드코어적인 시도록 할 수 있는 고난이도의 플레이역시 가능케할 수 있다.

2009. 5. 22.

문자열 뒤집기...

그냥..

(reverse "hello") -> "olleh"

임..

쩝.. 좀 긴 버전..

(defun string-reverse (str)
  (let ((strlen (length str)))
    (cond ((= strlen 0) str)
          ((= strlen 1) str)
          (t
           (string-concat (string (aref str (- strlen 1)))
                          (string-reverse (subseq str 0 (- strlen 1))))))))


iteration 버전?

(defun string-rev (str)
  (labels ((iter (src tar)
             (let ((strlen (length tar)))
               (cond ((= (length tar) 0) src)
                     (t
                      (iter (string-concat src (string (aref tar (- strlen 1))))
                            (subseq tar 0 (- strlen 1))))))))


머 C로 짤줄은 알지만.. 난 LISP을 사랑하니깐..

LISP으로 어드벤처 게임을... Ch 9

이번장에서는 실제로 키보드 입력을 받아들이고 게임을 구성하는 루프를 만든다.
먼저 키보드 입력을 받아들여보자.

;;; 사용자 키보드 입력을 체크하고 그에따란 이벤트 처리를 함
(defun keyboard-event ()
  (format t "What do you want to do:")
  (let ((k (read-line)))
    (if (= (length k) 1)
        k
        (keyboard-event))))

다음으로 이벤트 루프를 구성한다.

;;; 전체 이벤트 루프

(defun event-loop ()
  (let ((k (get-rooms-element (get-room *room*) #\m))
        (game-over nil))
    (loop
       (status-report)
       (let ((input-key (keyboard-event)))
         (cond ((string= input-key "n") (go-direction #\n))
               ((string= input-key "s") (go-direction #\s))
               ((string= input-key "e") (go-direction #\e))
               ((string= input-key "w") (go-direction #\w))
               ((string= input-key "c") (consume-food))
               ((string= input-key "f") (fight))
               ((string= input-key "r") (runaway))
               ((string= input-key "q") (setf game-over t))
               ((string= input-key "i") (inventory))))
       (if (eq game-over t)
           (return-from event-loop)))))


구성된 이벤트 루프를 토대로 해당하는 방으로 이동하는 함수를 만들고, 나머지 모든 부분을 연결하는 게임함수를 만든다.

;;; 해당 방의 방위로 이동하는 함수
(defun go-direction (direction)
  (let ((door (get-rooms-element (get-room *room*) direction)))
    (cond ((= door 0) (format t "There is no door in that direction."))
          (t
           (setf *room* door)))))
 
;;; 실제 게임 실행부분
(defun run-game ()
  ;;; 게임 초기화
  (reset-mobs)
  (init-var)
  (get-player-name)
  (put-gold 4)
  (put-mob 4)
  (set-init-treasure)
  (event-loop))

이제 슬슬 기본틀을 완성된 것 같고.. 음식과 전투, 도망가기, 인벤토리등을 구성하도록 하자