142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
package object
|
|
|
|
import (
|
|
"reflect"
|
|
"zworld/plugins/math/transform"
|
|
)
|
|
|
|
type Component interface {
|
|
// ID returns a unique identifier for this object.
|
|
ID() uint
|
|
|
|
// Name is used to identify the object within the scene.
|
|
Name() string
|
|
|
|
// Parent returns the parent of this object, or nil
|
|
Parent() Object
|
|
|
|
// Transform returns the object transform
|
|
Transform() transform.T
|
|
|
|
// Active indicates whether the object is active in the scene or not.
|
|
// E.g. the object/component and all its parents are enabled and active.
|
|
Active() bool
|
|
|
|
// Enabled indicates whether the object is currently enabled or not.
|
|
// Note that the object can still be inactive if an ancestor is disabled.
|
|
Enabled() bool
|
|
|
|
// Update the object. Called on every frame.
|
|
Update(Component, float32)
|
|
|
|
// Destroy the object
|
|
Destroy()
|
|
|
|
setName(string)
|
|
setParent(Object)
|
|
setEnabled(bool) bool
|
|
setActive(bool) bool
|
|
}
|
|
|
|
type component struct {
|
|
id uint
|
|
name string
|
|
enabled bool
|
|
active bool
|
|
parent Object
|
|
}
|
|
|
|
func emptyComponent(name string) component {
|
|
return component{
|
|
id: ID(),
|
|
name: name,
|
|
enabled: true,
|
|
active: false,
|
|
}
|
|
}
|
|
|
|
// componentType caches a reference to Component's reflect.Type
|
|
var componentType = reflect.TypeOf((*Component)(nil)).Elem()
|
|
|
|
func NewComponent[K Component](cmp K) K {
|
|
t := reflect.TypeOf(cmp).Elem()
|
|
v := reflect.ValueOf(cmp).Elem()
|
|
|
|
// find & initialize base component
|
|
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 == componentType {
|
|
// the components directly extends the base component
|
|
// if its nil, create a new empty component base
|
|
if value.IsZero() {
|
|
base := emptyComponent(t.Name())
|
|
value.Set(reflect.ValueOf(&base))
|
|
}
|
|
} else if _, isComponent := value.Interface().(Component); isComponent {
|
|
// this object extends some other non-base object
|
|
} else {
|
|
// its not an object, move on
|
|
continue
|
|
}
|
|
|
|
baseIdx = i
|
|
}
|
|
if baseIdx < 0 {
|
|
panic("struct does not embed a Component")
|
|
}
|
|
|
|
return cmp
|
|
}
|
|
|
|
func (b *component) ID() uint {
|
|
return b.id
|
|
}
|
|
|
|
func (b *component) Update(scene Component, dt float32) {
|
|
}
|
|
|
|
func (b *component) Transform() transform.T {
|
|
if b.parent == nil {
|
|
return transform.Identity()
|
|
}
|
|
return b.parent.Transform()
|
|
}
|
|
|
|
func (b *component) Active() bool { return b.active }
|
|
func (b *component) setActive(active bool) bool {
|
|
prev := b.active
|
|
b.active = active
|
|
return prev
|
|
}
|
|
|
|
func (b *component) Enabled() bool { return b.enabled }
|
|
func (b *component) setEnabled(enabled bool) bool {
|
|
prev := b.enabled
|
|
b.enabled = enabled
|
|
return prev
|
|
}
|
|
|
|
func (b *component) Parent() Object { return b.parent }
|
|
func (b *component) setParent(p Object) { b.parent = p }
|
|
|
|
func (b *component) setName(n string) { b.name = n }
|
|
func (b *component) Name() string { return b.name }
|
|
func (b *component) String() string { return b.Name() }
|
|
|
|
func (o *component) Destroy() {
|
|
if o.parent != nil {
|
|
o.parent.detach(o)
|
|
}
|
|
}
|