`(kakko ,man)

Find a guide into tomorrow by taking lessons from the past

Lisp Game Programming 2 <Stage 13-2 Barrage>

Stage1のボスキャラの弾幕(ショットパターン:6、7)
f:id:tomekame0126:20151019222841p:plain
Stage2のボスキャラの弾幕(ショットパターン:8、9、10)
※8と9は32方向に円形の弾幕を張り、10はshipに向かって10連発
f:id:tomekame0126:20151019222924p:plain
ミドルクラスの敵の弾幕(ショットパターン:3、4)
※3は回転しながら4方向に5連発、4はshipに向かって5連発
f:id:tomekame0126:20151019222931p:plain
Stage3のボスキャラの弾幕(ショットパターン:11、12、13)
f:id:tomekame0126:20151021232402p:plain
f:id:tomekame0126:20151028220154p:plain
さて今回作成したものは以下。
先に作成したショットパターンを読み込むためのプログラムを追加。

;; step13 <Enemy Shot Data>
;; -----------------------------------------------------------------------------------------------  
(load "C:\\work\\shot-data.lisp")

ショットパターンのためのクラスは、データ保存のために作成。

;;step13 <Enemy Shot Pattern>
;; -----------------------------------------------------------------------------------------------
(define-class shotpattern ()
  (timing angle-store shot-counter beforetime pattern-number pattern-number-store 
   pattern-cnt pattern-cnt-store repeat-flag count-flag first-x first-y first-angle) 0)
 ; timing                shottiming
 ; angle-store           angle-store
 ; shot-counter          number of shot per battery 
 ; beforetime            interval before shot
 ; pattern-number        shotpattern0-13
 ; pattern-number-store  store pattern number
 ; pattern-cnt           number of shotpattern
 ; pattern-cnt-store     store pattern counter
 ; repeat-flag           0:once 1:repeat
 ; count-flag            repeat count
 ; first-x               x position of first shot
 ; first-y               y position of first shot
 ; first-angle           angle of first shot

敵クラスfoeは、ショットパターンも継承するように変更

(define-class foe (entity shotpattern)
  (move-cnt damage-cnt life-cnt kind) 0)
 ; move-cnt   moving counter      (distance)  
 ; damage-cnt enemy damage counter(wait)
 ; life-cnt   enemy life counter  (life time)
 ; kind       kind of enemy

Step9で作成した敵弾の扱いは以下のように大幅に変更。

;; Step13 <Charge Enemy Shot>
;; ---------------------------------------------------------------------------------------------
(defun Charge-enemy-shot (enemy enemy-manager)
  (dotimes (i (shotdata-number-battery (aref *shot-pattern-data* (pattern-number enemy))))
    (let ((enemy-shot (make-instance 'entity :id 4 :width 8 :height 8 :dx 0 :dy 0 :state 0)))
      (push enemy-shot (enemy-shot-list enemy-manager)))))

;; Step13 <Set Enemy center>
;; ---------------------------------------------------------------------------------------------
(defun Set-enemy-center (enemy enemy-shot)
  (case (kind enemy)
    ((1) ; small class enemy
      (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) ; middle class enemy
      (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) ; large class enemy
      (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

;; Step13 <Set Repeat Flag OFF>
;; ---------------------------------------------------------------------------------------------
(defun Set-repeat-flag-OFF (enemy)
  (case (id enemy)  ; if enemy is blue or purple, repeat-flag OFF    
    ((80 81 82 83 84 85)                
      (setf (repeat-flag enemy) 0))       
    ((50 51 52 53)
      (setf (repeat-flag enemy) 0))))

;; Step13 <Set Enemy Variables>
;; ---------------------------------------------------------------------------------------------
(defun Set-enemy-variables (enemy)
  (when (= (count-flag enemy) 0)   ; --- if flag ON, set variables
    (setf (shot-counter enemy)                                  ; set shotcounter    (number of shot per 1 pattern)
	  (shotdata-number-battery-shot (aref *shot-pattern-data* (pattern-number enemy))))
    (setf (timing enemy) (beforetime enemy))                    ; set shot beforetime 
    (setf (pattern-cnt-store enemy) (pattern-cnt enemy))        ; each enemy has 0-2 patterns
    (setf (pattern-number-store enemy) (pattern-number enemy))  ; store patternnumber
    (setf (count-flag enemy) 1)))   ; --- flag OFF, no more set
  
;; Step13 <Set Enemy Shot angle>
;; ---------------------------------------------------------------------------------------------
(defvar *range-x*)
(defvar *range-y*)
(defvar *distance*)

(defun Set-enemy-shot-angle (shotangle ship enemy enemy-shot)
  (let ((angle (nth shotangle (shotdata-battery-angle (aref *shot-pattern-data* (pattern-number enemy)))))
	(rotation-angle (shotdata-direction-battery-angle (aref *shot-pattern-data* (pattern-number enemy))))
   	(speed (shotdata-shotspeed (aref *shot-pattern-data* (pattern-number enemy))))
        (ship-x  (+ (x ship) (/ (width ship) 2)))                  ; ship x position
   	(ship-y  (+ (y ship) (/ (height ship) 2)))                 ; ship y position
   	(ene-shot-x (+ (x enemy-shot) (/ (width enemy-shot) 2)))   ; enemy-shot x position
   	(ene-shot-y (+ (y enemy-shot) (/ (height enemy-shot) 2)))) ; enemy-shot y position
    (case (shotdata-battery-direction (aref *shot-pattern-data* (pattern-number enemy)))
      ((0)  ; beneath
        (when (= rotation-angle 0)  ; not rotation
	     (setf (dx enemy-shot) (* (cos (degree-radian angle)) speed)) ; dx from angle list
	     (setf (dy enemy-shot) (* (sin (degree-radian angle)) speed))); dy from angle list
	(when (/= rotation-angle 0) ; rotation
	     (if (= shotangle 0)
	       (setf (angle-store enemy) (+ (angle-store enemy) rotation-angle)))	
	     (setf (dx enemy-shot) (* (cos (degree-radian (+ angle (angle-store enemy)))) speed)) ; dx from angle list
	     (setf (dy enemy-shot) (* (sin (degree-radian (+ angle (angle-store enemy)))) speed)))); dy from angle list
      ((1)  ; direction of ship
	(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 (first-x enemy) (* (/ *range-x* *distance*) speed))    ; x distance from enemy to ship
        (setf (first-y enemy) (* (/ *range-y* *distance*) speed))    ; y distance form enemy to ship  
        (if (< (atan (/ (first-y enemy) (first-x enemy))) 0)         ; find angle in Arc tangent
          (setf (first-angle enemy) (radian-degree (+ (atan (/ (first-y enemy) (first-x enemy))) (/ pi 2))))
          (setf (first-angle enemy) (radian-degree (- (atan (/ (first-y enemy) (first-x enemy))) (/ pi 2)))))
        (when (= rotation-angle 0)  ; not rotation	  
	   (setf (dx enemy-shot) (* (cos (degree-radian (+ angle (first-angle enemy)))) speed))
	   (setf (dy enemy-shot) (* (sin (degree-radian (+ angle (first-angle enemy)))) speed)))
        (when (/= rotation-angle 0) ; rotation 
	     (if (= shotangle 0)
	       (setf (angle-store enemy) (+ (angle-store enemy) rotation-angle)))	   
	   (setf (dx enemy-shot) 
		 (* (cos (degree-radian (+ angle (first-angle enemy) (angle-store enemy)))) speed)) ; dx from angle list
	   (setf (dy enemy-shot)
		 (* (sin (degree-radian (+ angle (first-angle enemy) (angle-store enemy)))) speed))))))); dy from angle list

;; Step13 <Set Enemy Shot Timing>
;; ---------------------------------------------------------------------------------------------
(defun Set-enemy-shot-timing (ship enemy enemy-manager) 
  (when(and (= (move-cnt enemy) (timing enemy))            ; equal move-cnt and timing 
	    (= (repeat-flag enemy) 1))                     ; and shot repeat
    (let ((shotangle 0))                                   ; set enemy shot direction (start <- 0)
      (dolist (enemy-shot (enemy-shot-list enemy-manager))
        (when (= (state enemy-shot) 0)      
	  (Set-enemy-center enemy enemy-shot)                      ; set enemy center position
	  (Set-enemy-shot-angle shotangle ship enemy enemy-shot)   ; set enemy shot angle
	  (incf shotangle)                                         ; judge length of battery-angle-list
	  (if (= shotangle (length (shotdata-battery-angle (aref *shot-pattern-data* (pattern-number enemy)))))
	    (setf shotangle 0))                                    ; reset shotangle
	  (setf (state enemy-shot) 1))))                           ; shot state ON into battery-angle list
    (setf (shot-counter enemy) (decf (shot-counter enemy))) ; go to next shot timing
    (cond ((/= (shot-counter enemy) 0)            ; when shot counter not 0
	    (setf (timing enemy)                 ; set betweentime
	          (+ (timing enemy) (shotdata-betweentime (aref *shot-pattern-data* (pattern-number enemy))))))
          ((= (shot-counter enemy) 0)             ; when  shot counter 0
	    (setf (timing enemy)                 ; set aftertime
	          (+ (timing enemy) (shotdata-aftertime (aref *shot-pattern-data* (pattern-number enemy)))))
	    (setf (angle-store enemy) 0)    ; angle-store reset 0
	    (setf (pattern-cnt enemy) (decf (pattern-cnt enemy)))       ; go to next pattern  (ex: 1 -> 0)
	    (Set-repeat-flag-OFF enemy))))) ; blue and purple enemy repeat OFF

;; Step13 <Set Enemy Shot Pattern>
;; ---------------------------------------------------------------------------------------------
(defun Set-enemy-shot-pattern (enemy)
  (when (= (shot-counter enemy) 0)
    (if (= (pattern-cnt enemy) 0)	      ; reset shot pattern 
        (progn	  
          (setf (pattern-cnt enemy) (pattern-cnt-store enemy))        ; back to first pattern count
          (setf (pattern-number enemy) (pattern-number-store enemy))  ; back to first pattern number   
          (setf (shot-counter enemy)                ; back to first shotcounter
   	        (shotdata-number-battery-shot (aref *shot-pattern-data* (pattern-number enemy)))))
	(progn	    
          (setf (pattern-number enemy) (incf (pattern-number enemy))) ; pattern number + 1 (ex: 3 -> 4 etc)   
          (setf (shot-counter enemy)               ; set shotcounter (number of shot per 1 pattern)
	        (shotdata-number-battery-shot (aref *shot-pattern-data* (pattern-number enemy))))))))

なお、弾幕を設定するにあたり、Step7も以下のように各敵ごとのショットパターンの設定を行った。

茶色の敵:連射  :初弾までのタイミング  8 :ショットパターン 0
紫色の敵:単射  :初弾までのタイミング  8 :ショットパターン 1
青色の敵:単射  :初弾までのタイミング 16 :ショットパターン 2
灰色の敵:連射  :初弾までのタイミング 32 :ショットパターン 3  4
緑色の敵:連射  :初弾までのタイミング 32 :ショットパターン 5
ボス1 :連射  :初弾までのタイミング 64 :ショットパターン 6  7
ボス2 :連射  :初弾までのタイミング 64 :ショットパターン 8  9 10
ボス3 :連射  :初弾までのタイミング 64 :ショットパターン 11 12 13 

;; step7 <Generate Enemy> + Enemy Shot
;; -----------------------------------------------------------------------------------------------
(defgeneric Generate-enemy-item (map enemy-manager item-manager))
(defmethod Generate-enemy-item (map enemy-manager item-manager)
  (when (and (eql *enemy-generate-flag* t)
	     (= (mod *scroll-cnt* 64) 0)
             (<= (length (enemy-list enemy-manager)) 10)); max 10 enemy
     (dotimes (j 10)
       (when (/= (aref map *enemy-map-pointer* j) -1)
         (case (aref map *enemy-map-pointer* j)
                 ((7)                              ; when id is 7
                   (let ((enemy (make-instance 'foe      ; small class yellow enemy generate
                                 :id (aref map *enemy-map-pointer* j)
                                 :x (+ 160 (* j 32)) :y 0 :width 32 :height 32
                                 :life-cnt 3 :kind 1 :state 1
				 :beforetime 8 :pattern-number 0 :pattern-cnt 1 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
	         ((80)                              ; when id is 80
                   (let ((enemy (make-instance 'foe      ; small class purple enemy generate
                                 :id (aref map *enemy-map-pointer* j)
                                 :x (+ 160 (* j 32)) :y 0 :width 32 :height 32
                                 :life-cnt 3 :kind 1 :state 1
		                 :beforetime 8 :pattern-number 1 :pattern-cnt 1 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
		 ((50)                              ; when id is 50
                   (let ((enemy (make-instance 'foe      ; small class blue enemy generate
                                 :id (aref map *enemy-map-pointer* j)
                                 :x (+ 160 (* j 32)) :y 0 :width 32 :height 32
                                 :life-cnt 3 :kind 1 :state 1
		                 :beforetime 16 :pattern-number 2 :pattern-cnt 1 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))		
                 ((76)                                ; when id is 76
                   (let ((enemy (make-instance 'foe      ; middle class gray enemy generate
                                 :id (aref map *enemy-map-pointer* j)
                                 :x (+ 160 (* j 32)) :y 0 :width 64 :height 64
                                 :life-cnt 20 :kind 2 :state 1
				 :beforetime 32 :pattern-number 3 :pattern-cnt 2 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
		 ((78)                                ; when id is 78
                   (let ((enemy (make-instance 'foe      ; middle class green enemy generate
                                 :id (aref map *enemy-map-pointer* j)
                                 :x (+ 160 (* j 32)) :y 0 :width 64 :height 64
                                 :life-cnt 20 :kind 2 :state 1
				 :beforetime 32 :pattern-number 5 :pattern-cnt 1 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
                 ((70)                             ; when id is 70
                   (let ((enemy (make-instance 'foe      ; large class boss1 enemy generate
                                  :id (aref map *enemy-map-pointer* j)
                                  :x (+ 160 (* j 32)) :y 0 :width 96 :height 96
                                  :life-cnt 300 :kind 3 :state 1
				  :beforetime 64 :pattern-number 6 :pattern-cnt 2 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
		 ((71)                             ; when id is 71
                   (let ((enemy (make-instance 'foe      ; large class boss2 enemy generate 
                                  :id (aref map *enemy-map-pointer* j)
                                  :x (+ 160 (* j 32)) :y 0 :width 96 :height 96
                                  :life-cnt 300 :kind 3 :state 1
				  :beforetime 64 :pattern-number 8 :pattern-cnt 3 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
		 ((72)                             ; when id is 72
                   (let ((enemy (make-instance 'foe      ; large class boss3 enemy generate
                                  :id (aref map *enemy-map-pointer* j)
                                  :x (+ 160 (* j 32)) :y 0 :width 96 :height 96
                                  :life-cnt 300 :kind 3 :state 1
				  :beforetime 64 :pattern-number 11 :pattern-cnt 3 :repeat-flag 1)))
                   (push enemy (enemy-list enemy-manager))))
		 ((17 18)                                ; when id is 17 or 18
                   (let ((item (make-instance 'foe       ; item generate
                                  :id (aref map *enemy-map-pointer* j)
                                  :x (+ 160 (* j 32)) :y 0 :width 32 :height 32 :dx 0 :dy 2 :kind 4 :state 1)))
                   (push item (item-list item-manager)))))))     ; store items into item-list
     (if (/= *enemy-map-pointer* 0)
       (decf *enemy-map-pointer*)                        ; attention ! *enemy-map-pointer* 0 keep!
       (setf *enemy-generate-flag* nil))))               ; *enemy-map-pointer* 64 -> 0 (end position)  

H27.10.28追記
stage3の渦巻き型の弾幕がなかなか見事なので掲載。