Code: Select all
(defclass block ()
(x y ; Location of upper-left corner
w h ; Dimensions
vx vy ; Movement speed in pixels per second
last-update-time)) ; Time at last movement update as returned by (sdl2:get-ticks), milliseconds
Code: Select all
; Elided for brevity
(defun blocks-will-collide-p ((b1 block) (b2 block))
(with-slots ((x1 x) (y1 y) (w1 w) (h1 h) ...) b1
(with-slots ((x2 x) ...) b2
( #| collision check |# ))))
I tried to simplify this particular case with a macro called with-object I wrote, but I'm really wondering if there's a cleaner or more "standard" solution, besides explicitly typing out every object and slot combination. See working code below for the solution I came up with.
Code: Select all
;;; Supporting macro definition for (with-object...)
(require :closer-mop)
(require :alexandria)
(defun all-direct-slots (class)
"Returns list of all slots for a given class."
;Credits: This answer: https://stackoverflow.com/a/38473905
(append (closer-mop:class-direct-slots class)
(alexandria:mappend #'all-direct-slots
(closer-mop:class-direct-superclasses class))))
(defun all-direct-slot-names (class)
"Returns list of all slot names for a given object"
(mapcar #'closer-mop:slot-definition-name
(all-direct-slots class)))
(defmacro with-object ((name object class &optional (separator ".")) &body body)
"Binds symbols such that C++-style dot syntax can be used for slots.
Example usage:
(defclass tst () (x y z))
(setf mx (make-instance 'tst))
(with-object (var mx tst) (setf var.x 1 var.y 2 var.z 3))"
(let* ((slot-names (all-direct-slot-names (find-class class)))
(new-slot-symbols (mapcar (lambda (sname)
(intern (concatenate 'string
(symbol-name name)
separator
(symbol-name sname))))
slot-names)))
`(with-slots ,(map 'list #'list new-slot-symbols slot-names)
,object
,@body)))
;;; Working future-collision function
(require :sdl2)
(defmethod block-will-collide-p ((b1 block) (b2 block))
(with-object (a b1 block)
(with-object (b b2 block)
(let* ((time-a (- (sdl2:get-ticks) a.last-update-tick))
(a.xp (truncate (+ a.x (* 1/1000 a.vx time-a))))
(a.yp (truncate (+ a.y (* 1/1000 a.vy time-a))))
(time-b (- (sdl2:get-ticks) b.last-update-tick))
(b.xp (truncate (+ b.x (* 1/1000 b.vx time-b))))
(b.yp (truncate (+ b.y (* 1/1000 b.vy time-b)))))
(sdl2:has-intersect (sdl2:make-rect a.xp a.yp a.w a.h)
(sdl2:make-rect b.xp b.yp b.w b.h))))))