zworld/engine/object/component.go
2024-01-14 22:56:06 +08:00

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)
}
}