229 lines
4.6 KiB
Go
229 lines
4.6 KiB
Go
|
|
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)
|
||
|
|
}
|
||
|
|
}
|