cl-openglで入力を拾う


#テーマ

皆さんこんにちわ、今回はcl-openglで入力を拾って簡単なゲーム(所謂テニスゲームってやつ)を作ってみたいと思います。この記事書きながら作っていたので矛盾のあること書いてたりすることがあるかもしれませんがそこは生暖かい目（ ゜゜）で見てやってくださいませ。

#入力を拾う

早速ですが、cl-openglで入力を拾うためのコードです。
グローバル変数に結果を保存してますが、テストなんで許してね。

```cl
;;;; 以下のコードで入力が拾えます。
;; keyboard
;; Executed when key pressed.
(defmethod glut:keyboard ((window main-window) key x y)
	(case key
		(#\Esc (glut:destroy-current-window))
		(#\z (setq *key-z* t))
		(#\c (setq *key-c* t))
	))

;; keyboard-up
;; Executed when key released.
(defmethod glut:keyboard-up ((window main-window) key x y )
	(case key
		(#\z (setq *key-z* nil))
		(#\c (setq *key-c* nil))
	))
```

マウスの入力を拾うにはこう。

```cl
;; mouse
(defmethod glut:mouse ((window main-window) button state x y)
  (setf *mouse-left-down* (and (equal button :LEFT-BUTTON) (equal state :DOWN)))
  (setf *mouse-right-down* (and (equal button :RIGHT-BUTTON) (equal state :DOWN))))

;; detect mouse move
(defmethod glut:passive-motion ((window main-window) x y)
  (setq *mouse-x* x)
  (setq *mouse-y* y))

;; detect mouse drag
(defmethod glut:motion ((window main-window) x y)
  (setq *mouse-x* x)
  (setq *mouse-y* y))
```

上記のようなコードで今回作るものには充分な入力が拾えると思います。

では早速使って見ましょう。

描画周りに使用する関数群は前回の記事で
使用したglib.lispにいくらかコードを追加したものを使います。

ソースを載せておきます。

***

```cl:glib.lisp

;; Load librarlies
(require 'asdf)
(require 'cl-opengl)
(require 'cl-glut)
(require 'cl-glu)

;; Parameters
(defvar *width* 500)
(defvar *height* 500)
(defparameter *key-z* nil)
(defparameter *key-c* nil)


;; USER FUNCTIONS
(defun user-display () ())
(defun user-init () ())
(defun user-idle () ())


;; Derived window class
(defclass main-window (glut:window) ()
	(:default-initargs :title "opengl test" :mode '(:double :rgb :depth) :width *width* :height *height*))


;; keyboard
;; Executed when key pressed.
(defmethod glut:keyboard ((window main-window) key x y)
	;(declare (ignore x y))
	(case key
		(#\Esc (glut:destroy-current-window))
		(#\z (setq *key-z* t))
		(#\c (setq *key-c* t))
	))

;; keyboard-up
;; Executed when key released.
(defmethod glut:keyboard-up ((window main-window) key x y )
	(case key
		(#\z (setq *key-z* nil))
		(#\c (setq *key-c* nil))
	))

;; glut:display
;; Draw.
(defmethod glut:display ((window main-window))
	;; Clear buffer
	(gl:clear :color-buffer :depth-buffer)
	
	;; Draw shape
	(gl:shade-model :flat)
	(gl:normal 0 0 1)

	;; user display process
	(user-display)

	;; Swap buffer
	(glut:swap-buffers))

;; glut:idle
;; Application idle.
(defmethod glut:idle ((window main-window))
	;; user idling process
	(user-idle)
	(glut:post-redisplay))

;; glut:reshape
(defmethod glut:reshape ((w main-window) width height)
	(gl:viewport 0 0 width height)
	(gl:load-identity)
	(glu:ortho-2d 0.0 *width* *height* 0.0))

;; glut-display
;; Draw.
(defmethod glut:display-window :before ((window main-window)) )

;; main
;; Main function (Program entry point).
(defun main ()
	;; user initalization process
	(user-init)
	(glut:display-window (make-instance 'main-window)))

;; Draw 2D line.
(defun g2line (x y x2 y2)
    (gl:with-primitives :lines
        (gl:vertex x y 0.0)
        (gl:vertex x2 y2 0.0)))


;; Draw 2D line.
(defun g2linec (x y x2 y2 r g b)
	(gl:with-primitives :lines
        (gl:color r g b)
		(gl:vertex x y 0.0)
		(gl:vertex x2 y2 0.0))
        (gl:color 1.0 1.0 1.0))


;; Draw filled rect
(defun g2fillrectc (x y x2 y2 r g b)
	(gl:with-primitives :polygon
        (gl:color r g b)
		(gl:vertex x y 0.0)
		(gl:vertex x2 y 0.0)
		(gl:vertex x2 y2 0.0)
		(gl:vertex x y2 0.0)
        (gl:color 1.0 1.0 1.0)))

;; iota
(defun iota (m &optional (n 0) (step 1))
  (if (>= n m)
    nil
    (cons n (iota m (+ n step) step))))
```

```cl:input_test.lisp
;; glibをロード
(load "glib.lisp")

#|
 | 初期化処理
 |#
(defun user-init ()
  (defparameter *bar-x* (/ *width* 2.0))
  (defparameter *bar-spd* 10.0)
  (defparameter *bar-size* 80.0)
  (defparameter *ball* (list (/ *width* 2.0) (/ *height* 2.0)))
  (defparameter *ball-spd* (list 0.0 2.0))
  (defparameter *bar-elev* 10.0)
  (defparameter *bar-width* 5.0)
  (defparameter *ball-max-spd* 10.0)
  (defparameter *count-max* 50)
  (defparameter *count* *count-max*))

#|
 | ウェイト処理
 |#
(defun user-idle ()
  (sleep (/ 1.0 60.0)))

#|
 | 描画処理
 |#
(defun user-display ()
  ;; barの処理
  (updt-bar)
  (draw-bar)

  ;; ballの処理
  (draw-ball)
  (updt-ball))

#|
 | barの描画
 |#
(defun draw-bar ()
  (g2fillrectc (- *bar-x* (/ *bar-size* 2.0)) 
			   (- *height* *bar-elev*) 
			   (+ *bar-x* (/ *bar-size* 2.0)) 
			   (+ (- *height* *bar-elev*) *bar-width*) 1.0 1.0 1.0))

#|
 | barの更新処理とキー操作の受け付け
 |#
(defun updt-bar ()
  (if *key-z* (setf *bar-x* (- *bar-x* *bar-spd*)) nil)
  (if *key-c* (setf *bar-x* (+ *bar-x* *bar-spd*)) nil))

#|
 | ballの描画処理
 |#
(defun draw-ball ()
  (g2fillrectc (- (car *ball*) 2.0) (- (cadr *ball*) 2.0)
			   (+ (car *ball*) 2.0) (+ (cadr *ball*) 2.0)
			   1.0 1.0 1.0))

#|
 | ballの更新処理
 |#
(defun updt-ball ()
  ;; ballを移動
  (setf (car *ball*) (+ (car *ball*) (car *ball-spd*)))
  (setf (cadr *ball*) (+ (cadr *ball*) (cadr *ball-spd*)))

  ;; ballを加速
  (if (< (cadr *ball-spd*) 0.0)
	(setf (cadr *ball-spd*) (+ (cadr *ball-spd*) -0.001))
	(setf (cadr *ball-spd*) (+ (cadr *ball-spd*) 0.001)))

  ;; barとの衝突判定
  (if (and (> (cadr *ball*) (- *height* *bar-width* *bar-elev*)) 
		   (< (cadr *ball*) (- *height* *bar-elev*)))
	(if (and (> (car *ball*) (- *bar-x* *bar-size*)) (< (car *ball*) (+ *bar-x* *bar-size*)))
	  (progn 
		(setf (cadr *ball-spd*) (* (cadr *ball-spd*) -1.0))
		(setf (cadr *ball*) (- *height* *bar-width* *bar-elev* 1.0)))
	  nil)
	nil)

  ;; ballの速度をランダムに変更する
  (setf *count* (- *count* 1))
  (if (< *count* 0) 
	(progn 
	  (setf (car *ball-spd*) (* (- (random 1.0) 0.5) *ball-max-spd*))
	  (setf *count* *count-max*))
	nil)

  ;; ballを画面内にとどめる
  (if (< *width* (car *ball*))
	(progn
	  (setf (car *ball*) (- *width* 1.0))
	  (setf (car *ball-spd*) (* (car *ball-spd*) -1.0)))
	nil)
  (if (> 0.0 (car *ball*))
	(progn
	  (setf (car *ball*) (+ 0.0 1.0))
	  (setf (car *ball-spd*) (* (car *ball-spd*) -1.0)))
	nil)
  (if (> 0.0 (cadr *ball*))
	(progn
	  (setf (cadr *ball*) (+ 0.0 1.0))
	  (setf (cadr *ball-spd*) (* (cadr *ball-spd*) -1.0)))
	nil))
```
***

#実行結果

![クリップボード01.png](https://qiita-image-store.s3.amazonaws.com/0/74246/cfc04973-a294-44d0-c820-c1b8ef1f41a1.png)


はい、鬼畜ゲーが誕生しました。
細かいところは作りこんではいないのでそこは目をつぶってね。

ZとCでバーを動かします。
ボールはランダムに速度を変え、徐々に早くなっていきます。

ちなみに`user-idle`で速度を調整しています、60fpsを維持できるようにしたいですがやり方をしりません。誰か教えてください。m(_ _)m


今回はここまで。
最後まで読んでくださった方々、ありがとうございました。

追記１
また環境を書き忘れました。
OS：Windows 7 64bit
処理系:SBCL 1.2.15 x86