-
Notifications
You must be signed in to change notification settings - Fork 136
/
scene.go
144 lines (118 loc) · 3.7 KB
/
scene.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package engo
import (
"fmt"
"reflect"
)
var scenes = make(map[string]*sceneWrapper)
// Scene represents a screen ingame.
// i.e.: main menu, settings, but also the game itself
type Scene interface {
// Preload is called before loading resources
Preload()
// Setup is called before the main loop
Setup(Updater)
// Type returns a unique string representation of the Scene, used to identify it
Type() string
}
// Shower is an optional interface a Scene can implement, indicating it'll have custom behavior
// whenever the Scene gets shown again after being hidden (due to switching to other Scenes)
type Shower interface {
// Show is called whenever the other Scene becomes inactive, and this one becomes the active one
Show()
}
// Hider is an optional interface a Scene can implement, indicating it'll have custom behavior
// whenever the Scene get hidden to make room fr other Scenes.
type Hider interface {
// Hide is called when an other Scene becomes active
Hide()
}
// Exiter is an optional interface a Scene can implement, indicating it'll have custom behavior
// whenever the game get closed.
type Exiter interface {
// Exit is called when the user or the system requests to close the game
// This should be used to cleanup or prompt user if they're sure they want to close
// To prevent the default action (close/exit) make sure to set OverrideCloseAction in
// your RunOpts to `true`. You should then handle the exiting of the program by calling
// engo.Exit()
Exit()
}
// Updater is an interface for what handles your game's Update during each frame.
// typically, this will be an *ecs.World, but you can implement your own Updater
// and use engo without using engo's ecs
type Updater interface {
Update(float32)
}
type sceneWrapper struct {
scene Scene
update Updater
mailbox *MessageManager
}
// CurrentScene returns the SceneWorld that is currently active
func CurrentScene() Scene {
return currentScene
}
// SetScene sets the currentScene to the given Scene, and
// optionally forcing to create a new ecs.World that goes with it.
func SetScene(s Scene, forceNewWorld bool) {
// Break down currentScene
if currentScene != nil {
if hider, ok := currentScene.(Hider); ok {
hider.Hide()
}
}
// Register Scene if needed
sceneMutex.RLock()
wrapper, registered := scenes[s.Type()]
sceneMutex.RUnlock()
if !registered {
RegisterScene(s)
sceneMutex.RLock()
wrapper = scenes[s.Type()]
sceneMutex.RUnlock()
}
// Initialize new Scene / World if needed
var doSetup bool
if wrapper.update == nil || forceNewWorld {
t := reflect.Indirect(reflect.ValueOf(currentUpdater)).Type()
v := reflect.New(t)
wrapper.update = v.Interface().(Updater)
wrapper.mailbox = &MessageManager{}
doSetup = true
}
// Do the switch
currentScene = s
currentUpdater = wrapper.update
Mailbox = wrapper.mailbox
// doSetup is true whenever we're (re)initializing the Scene
if doSetup {
s.Preload()
wrapper.mailbox.listeners = make(map[string][]HandlerIDPair)
s.Setup(wrapper.update)
} else {
if shower, ok := currentScene.(Shower); ok {
shower.Show()
}
}
}
// RegisterScene registers the `Scene`, so it can later be used by `SetSceneByName`
func RegisterScene(s Scene) {
sceneMutex.RLock()
_, ok := scenes[s.Type()]
sceneMutex.RUnlock()
if !ok {
sceneMutex.Lock()
scenes[s.Type()] = &sceneWrapper{scene: s}
sceneMutex.Unlock()
}
}
// SetSceneByName does a lookup for the `Scene` where its `Type()` equals `name`, and then sets it as current `Scene`
func SetSceneByName(name string, forceNewWorld bool) error {
sceneMutex.RLock()
scene, ok := scenes[name]
sceneMutex.RUnlock()
if !ok {
return fmt.Errorf("scene not registered: %s", name)
}
SetScene(scene.scene, forceNewWorld)
return nil
}