`(kakko ,man)

Find a guide into tomorrow by taking lessons from the past

Lisp Game Programming 2 <Stage 9>

前回ではオプションを取ると自分の周りをバルーンが回るように設定したが、敵も弾をださないとつまらないので以下のプログラムを作ってみた。

敵が発射する弾は1方向のみだが、自分の移動先をめがけて撃ってくる。
これを実現するために、Lispのsqrt関数を使用した。

ab-stage9.lispの一部 ⇒

Move-enemy-shotは、敵の弾を移動させ、ゲームフィールドの外に全部(8dot)出たらenemy-shotのstateを0(dead)にするためのもの

;; Step9 <Move Enemy Shot>
;; -----------------------------------------------------------------------------------------------
(defgeneric Move-enemy-shot (enemy-manager game-field))
(defmethod Move-enemy-shot (enemy-manager game-field)
  "enemy shot move"
  (dolist (enemy-shot (enemy-shot-list enemy-manager))               
    (when (= (state enemy-shot) 1)           ; set enemy-shot state alive
      (incf (x enemy-shot) (dx enemy-shot))
      (incf (y enemy-shot) (dy enemy-shot))
      (when (or (< (x enemy-shot) (- (field-x game-field) 8))
                (> (x enemy-shot) (width game-field))
                (< (y enemy-shot) (- (field-y game-field) 8))
                (> (y enemy-shot) (height game-field)))
        (setf (state enemy-shot) 0)))))

Set-enemy-shotは、enemyがゲームフィールド内にいるときで、かつ64回分移動したときに弾を3つの大きさ敵の中心部から
自分に向けて発射するためのもの

;; Step9 <Set Enemy Shot>
;; -----------------------------------------------------------------------------------------------
(defvar *range-x*)
(defvar *range-y*)
(defvar *distance*)

(defparameter *enemy-shot-max* 10)

(defgeneric Set-enemy-shot (enemy-manager ship game-field))
(defmethod Set-enemy-shot (enemy-manager ship game-field)
  "enemy shot appear position set and move"  
  (dolist (enemy (enemy-list enemy-manager)) 
    (when (and (= (state enemy) 1)
               (>= (x enemy) 0)
               (< (x enemy) (- (width game-field) (width enemy)))  ; 32 or 64 or 96
               (>= (y enemy) 0)
               (< (y enemy) (- (height game-field) (width enemy))) ; 32 or 64 or 96
               (= (mod (move-cnt enemy) 64) 0))
       (when (< (length (enemy-shot-list enemy-manager)) *enemy-shot-max*)                       
         (let ((enemy-shot (make-instance 'entity :id 4 :width 8 :height 8 :dx 0 :dy 6 :state 0)))
               (push enemy-shot (enemy-shot-list enemy-manager))))
       (dolist (enemy-shot (enemy-shot-list enemy-manager))
         (when (= (state enemy-shot) 0)
           (case (kind enemy)
             ((1)
               (setf (y enemy-shot) (+ (y enemy) 12))    ; center of small class enemy x
               (setf (x enemy-shot) (+ (x enemy) 12)))   ; center of small class enemy y 
             ((2)
               (setf (y enemy-shot) (+ (y enemy) 28))    ; center of middle class enemy x
               (setf (x enemy-shot) (+ (x enemy) 28)))   ; center of middle class enemy y 
             ((3)
	       (setf (y enemy-shot) (+ (y enemy) 44))    ; center of large class enemy x
               (setf (x enemy-shot) (+ (x enemy) 44))))  ; center of large class enemy y 
           (let ((ship-x  (+ (x ship) (/ (width ship) 2)))
                 (ship-y  (+ (y ship) (/ (height ship) 2)))
                 (ene-shot-x (+ (x enemy-shot) (/ (width enemy-shot) 2)))
                 (ene-shot-y (+ (y enemy-shot) (/ (height enemy-shot) 2))))
             (setf *range-x* (- ship-x ene-shot-x))
             (setf *range-y* (- ship-y ene-shot-y))
             (setf *distance* (sqrt (+ (* *range-x* *range-x*) (* *range-y* *range-y*))))
             (setf (dx enemy-shot) (floor (* (/ *range-x* *distance*) 6)))
             (setf (dy enemy-shot) (floor (* (/ *range-y* *distance*) 6))))
           (setf (state enemy-shot) 1))))))

Remove-dead-enemy-shotは、敵の弾のstateが0(dead)のものをdeleteするもの

;; Step9 <Remove Dead Enemy Shot>
;; -----------------------------------------------------------------------------------------------
(defgeneric Remove-dead-enemy-shot (enemy-manager))
(defmethod Remove-dead-enemy-shot (enemy-manager)
  "dead enemy shot remove from list"
  (setf (enemy-shot-list enemy-manager) 
	(delete-if #'(lambda (enemy-shot) (= (state enemy-shot) 0)) (enemy-shot-list enemy-manager))))

Draw-enemy-shotはその名のとおり、敵の弾がaliveのものを描画するもの

;; Step9 <Draw Enemy Shot>
;; -----------------------------------------------------------------------------------------------
(defgeneric Draw-enemy-shot (enemy-manager))
(defmethod Draw-enemy-shot (enemy-manager)
  (dolist (enemy-shot (enemy-shot-list enemy-manager))
    (when (= (state enemy-shot) 1)
      (Draw enemy-shot))))

当然、以下のコードも本体のルーチンに追加する必要がある。

	  ; <Enemy-shot :Move Set Draw Remove>  
	    (Move-enemy-shot enemy-manager game-field)
	    (Set-enemy-shot enemy-manager ship game-field)
            (Draw-enemy-shot enemy-manager)               ; draw enemy shot
	    (Remove-dead-enemy-shot enemy-manager)

なお、Set-enemy-shotの中の、敵の弾の発射カウント(move-cnt enemy)は、先に登場した、Move-Enemyメソットの中で
カウントのためのincfを行っている。
もう少しプログラムを追加したら、全部のソースコードを見て一度全体像を把握することにしよう。

f:id:tomekame0126:20150712194101p:plain