zworld/engine/object/actor.go

229 lines
4.6 KiB
Go
Raw Normal View History

2024-01-14 22:56:06 +08:00
package object
import (
"reflect"
"zworld/plugins/math/transform"
"zworld/plugins/system/input"
"zworld/plugins/system/input/keys"
"zworld/plugins/system/input/mouse"
)
type Object interface {
Component
input.Handler
// Children returns a slice containing the objects children.
Children() []Component
attach(...Component)
detach(Component)
}
// objectType caches a reference to Object's reflect.Type
var objectType = reflect.TypeOf((*Object)(nil)).Elem()
type object struct {
component
transform transform.T
children []Component
}
func emptyObject(name string) object {
return object{
component: emptyComponent(name),
transform: transform.Identity(),
}
}
// Empty creates a new, empty object.
func Empty(name string) Object {
obj := emptyObject(name)
return &obj
}
func New[K Object](name string, obj K) K {
t := reflect.TypeOf(obj).Elem()
v := reflect.ValueOf(obj).Elem()
// find & initialize base object
baseIdx := -1
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if !field.Anonymous {
// only anonymous fields are considered
continue
}
if !field.IsExported() {
// only exported fields can be base fields
continue
}
value := v.Field(i)
if field.Type == objectType {
// the object directly extends the base object
// if its nil, create a new empty object base
if value.IsZero() {
base := Empty(name)
value.Set(reflect.ValueOf(base))
}
} else if _, isObject := value.Interface().(Object); isObject {
// this object extends some other non-base object
} else {
// its not an object, move on
continue
}
// if we already found a base field, the user has embedded multiple objects
if baseIdx >= 0 {
panic("struct embeds multiple Object types")
}
baseIdx = i
}
if baseIdx < 0 {
panic("struct does not embed an Object")
}
// add Component fields as children
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if i == baseIdx {
continue
}
if !field.IsExported() {
continue
}
// all uninitialized fields are ignored since they cant contain valid component references
value := v.Field(i)
if value.IsZero() {
continue
}
// the field contains a reference to an instantiated component
// if its an orphan, add it to the object's children
if child, ok := value.Interface().(Component); ok {
if child.Parent() == nil {
Attach(obj, child)
}
}
}
return obj
}
func (g *object) Transform() transform.T {
return g.transform
}
func (o *object) setParent(parent Object) {
// check for cycles
ancestor := parent
for ancestor != nil {
if ancestor.ID() == o.ID() {
panic("cyclical object hierarchies are not allowed")
}
ancestor = ancestor.Parent()
}
o.component.setParent(parent)
if parent != nil {
o.transform.SetParent(parent.Transform())
} else {
o.transform.SetParent(nil)
}
}
func (g *object) Update(scene Component, dt float32) {
for _, child := range g.children {
if child.Enabled() {
child.Update(scene, dt)
}
}
}
func (g *object) Children() []Component {
return g.children
}
func (g *object) attach(children ...Component) {
for _, child := range children {
g.attachIfNotChild(child)
}
}
func (g *object) attachIfNotChild(child Component) {
for _, existing := range g.children {
if existing.ID() == child.ID() {
return
}
}
g.children = append(g.children, child)
}
func (g *object) detach(child Component) {
for i, existing := range g.children {
if existing.ID() == child.ID() {
g.children = append(g.children[:i], g.children[i+1:]...)
return
}
}
}
func (g *object) setActive(active bool) bool {
wasActive := g.component.setActive(active)
if active {
for _, child := range g.children {
activate(child)
}
} else {
for _, child := range g.children {
deactivate(child)
}
}
return wasActive
}
func (g *object) KeyEvent(e keys.Event) {
for _, child := range g.children {
if !child.Enabled() {
continue
}
if handler, ok := child.(input.KeyHandler); ok {
handler.KeyEvent(e)
if e.Handled() {
return
}
}
}
}
func (g *object) MouseEvent(e mouse.Event) {
for _, child := range g.children {
if !child.Enabled() {
continue
}
if handler, ok := child.(input.MouseHandler); ok {
handler.MouseEvent(e)
if e.Handled() {
return
}
}
}
}
func (o *object) Destroy() {
// iterate over a copy of the child slice, since it will be mutated
// when the child detaches itself during destruction
children := make([]Component, len(o.Children()))
copy(children, o.Children()[:])
for _, child := range o.Children() {
child.Destroy()
}
if o.parent != nil {
o.parent.detach(o)
}
}