Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
Pokeprofs
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
william.ho
Pokeprofs
Commits
50ca7c78
Commit
50ca7c78
authored
1 year ago
by
william.ho
Browse files
Options
Downloads
Patches
Plain Diff
menu.rs
parent
df2b0ed6
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
game/src/main.rs
+1
-569
1 addition, 569 deletions
game/src/main.rs
game/src/menu.rs
+567
-0
567 additions, 0 deletions
game/src/menu.rs
with
568 additions
and
569 deletions
game/src/main.rs
+
1
−
569
View file @
50ca7c78
...
@@ -58,575 +58,7 @@ fn setup(mut commands: Commands) {
...
@@ -58,575 +58,7 @@ fn setup(mut commands: Commands) {
mod
game
;
mod
game
;
mod
menu
{
mod
menu
;
use
bevy
::{
app
::
AppExit
,
prelude
::
*
};
use
super
::{
despawn_screen
,
DisplayQuality
,
GameState
,
Volume
,
TEXT_COLOR
};
// This plugin manages the menu, with 5 different screens:
// - a main menu with "New Game", "Settings", "Quit"
// - a settings menu with two submenus and a back button
// - two settings screen with a setting that can be set and a back button
pub
struct
MenuPlugin
;
impl
Plugin
for
MenuPlugin
{
fn
build
(
&
self
,
app
:
&
mut
App
)
{
app
// At start, the menu is not enabled. This will be changed in `menu_setup` when
// entering the `GameState::Menu` state.
// Current screen in the menu is handled by an independent state from `GameState`
.add_state
::
<
MenuState
>
()
.add_systems
(
OnEnter
(
GameState
::
Menu
),
menu_setup
)
// Systems to handle the main menu screen
.add_systems
(
OnEnter
(
MenuState
::
Main
),
main_menu_setup
)
.add_systems
(
OnExit
(
MenuState
::
Main
),
despawn_screen
::
<
OnMainMenuScreen
>
)
// Systems to handle the settings menu screen
.add_systems
(
OnEnter
(
MenuState
::
Settings
),
settings_menu_setup
)
.add_systems
(
OnExit
(
MenuState
::
Settings
),
despawn_screen
::
<
OnSettingsMenuScreen
>
,
)
// Systems to handle the display settings screen
.add_systems
(
OnEnter
(
MenuState
::
SettingsDisplay
),
display_settings_menu_setup
,
)
.add_systems
(
Update
,
(
setting_button
::
<
DisplayQuality
>
.run_if
(
in_state
(
MenuState
::
SettingsDisplay
)),
),
)
.add_systems
(
OnExit
(
MenuState
::
SettingsDisplay
),
despawn_screen
::
<
OnDisplaySettingsMenuScreen
>
,
)
// Systems to handle the sound settings screen
.add_systems
(
OnEnter
(
MenuState
::
SettingsSound
),
sound_settings_menu_setup
)
.add_systems
(
Update
,
setting_button
::
<
Volume
>
.run_if
(
in_state
(
MenuState
::
SettingsSound
)),
)
.add_systems
(
OnExit
(
MenuState
::
SettingsSound
),
despawn_screen
::
<
OnSoundSettingsMenuScreen
>
,
)
// Common systems to all screens that handles buttons behavior
.add_systems
(
Update
,
(
menu_action
,
button_system
)
.run_if
(
in_state
(
GameState
::
Menu
)),
);
}
}
// State used for the current menu screen
#[derive(Clone,
Copy,
Default,
Eq,
PartialEq,
Debug,
Hash,
States)]
enum
MenuState
{
Main
,
Settings
,
SettingsDisplay
,
SettingsSound
,
#[default]
Disabled
,
}
// Tag component used to tag entities added on the main menu screen
#[derive(Component)]
struct
OnMainMenuScreen
;
// Tag component used to tag entities added on the settings menu screen
#[derive(Component)]
struct
OnSettingsMenuScreen
;
// Tag component used to tag entities added on the display settings menu screen
#[derive(Component)]
struct
OnDisplaySettingsMenuScreen
;
// Tag component used to tag entities added on the sound settings menu screen
#[derive(Component)]
struct
OnSoundSettingsMenuScreen
;
const
NORMAL_BUTTON
:
Color
=
Color
::
rgb
(
0.15
,
0.15
,
0.15
);
const
HOVERED_BUTTON
:
Color
=
Color
::
rgb
(
0.25
,
0.25
,
0.25
);
const
HOVERED_PRESSED_BUTTON
:
Color
=
Color
::
rgb
(
0.25
,
0.65
,
0.25
);
const
PRESSED_BUTTON
:
Color
=
Color
::
rgb
(
0.35
,
0.75
,
0.35
);
// Tag component used to mark which setting is currently selected
#[derive(Component)]
struct
SelectedOption
;
// All actions that can be triggered from a button click
#[derive(Component)]
enum
MenuButtonAction
{
Play
,
Settings
,
SettingsDisplay
,
SettingsSound
,
BackToMainMenu
,
BackToSettings
,
Quit
,
}
// This system handles changing all buttons color based on mouse interaction
fn
button_system
(
mut
interaction_query
:
Query
<
(
&
Interaction
,
&
mut
BackgroundColor
,
Option
<&
SelectedOption
>
),
(
Changed
<
Interaction
>
,
With
<
Button
>
),
>
,
)
{
for
(
interaction
,
mut
color
,
selected
)
in
&
mut
interaction_query
{
*
color
=
match
(
*
interaction
,
selected
)
{
(
Interaction
::
Pressed
,
_
)
|
(
Interaction
::
None
,
Some
(
_
))
=>
PRESSED_BUTTON
.into
(),
(
Interaction
::
Hovered
,
Some
(
_
))
=>
HOVERED_PRESSED_BUTTON
.into
(),
(
Interaction
::
Hovered
,
None
)
=>
HOVERED_BUTTON
.into
(),
(
Interaction
::
None
,
None
)
=>
NORMAL_BUTTON
.into
(),
}
}
}
// This system updates the settings when a new value for a setting is selected, and marks
// the button as the one currently selected
fn
setting_button
<
T
:
Resource
+
Component
+
PartialEq
+
Copy
>
(
interaction_query
:
Query
<
(
&
Interaction
,
&
T
,
Entity
),
(
Changed
<
Interaction
>
,
With
<
Button
>
)
>
,
mut
selected_query
:
Query
<
(
Entity
,
&
mut
BackgroundColor
),
With
<
SelectedOption
>>
,
mut
commands
:
Commands
,
mut
setting
:
ResMut
<
T
>
,
)
{
for
(
interaction
,
button_setting
,
entity
)
in
&
interaction_query
{
if
*
interaction
==
Interaction
::
Pressed
&&
*
setting
!=
*
button_setting
{
let
(
previous_button
,
mut
previous_color
)
=
selected_query
.single_mut
();
*
previous_color
=
NORMAL_BUTTON
.into
();
commands
.entity
(
previous_button
)
.remove
::
<
SelectedOption
>
();
commands
.entity
(
entity
)
.insert
(
SelectedOption
);
*
setting
=
*
button_setting
;
}
}
}
fn
menu_setup
(
mut
menu_state
:
ResMut
<
NextState
<
MenuState
>>
)
{
menu_state
.set
(
MenuState
::
Main
);
}
fn
main_menu_setup
(
mut
commands
:
Commands
,
asset_server
:
Res
<
AssetServer
>
)
{
// Common style for all buttons on the screen
let
button_style
=
Style
{
width
:
Val
::
Px
(
250.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_icon_style
=
Style
{
width
:
Val
::
Px
(
30.0
),
// This takes the icons out of the flexbox flow, to be positioned exactly
position_type
:
PositionType
::
Absolute
,
// The icon will be close to the left border of the button
left
:
Val
::
Px
(
10.0
),
right
:
Val
::
Auto
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnMainMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Display the game name
parent
.spawn
(
TextBundle
::
from_section
(
"Poke Profs
\n
Menu"
,
TextStyle
{
font_size
:
80.0
,
color
:
TEXT_COLOR
,
..
default
()
},
)
.with_style
(
Style
{
margin
:
UiRect
::
all
(
Val
::
Px
(
50.0
)),
..
default
()
}),
);
// Display three buttons for each action available from the main menu:
// - new game
// - settings
// - quit
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Play
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/right.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
.clone
(),
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"New Game"
,
button_text_style
.clone
(),
));
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Settings
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/wrench.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
.clone
(),
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"Settings"
,
button_text_style
.clone
(),
));
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Quit
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/exitRight.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
,
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"Quit"
,
button_text_style
));
});
});
});
}
fn
settings_menu_setup
(
mut
commands
:
Commands
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnSettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
for
(
action
,
text
)
in
[
(
MenuButtonAction
::
SettingsDisplay
,
"Display"
),
(
MenuButtonAction
::
SettingsSound
,
"Sound"
),
(
MenuButtonAction
::
BackToMainMenu
,
"Back"
),
]
{
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
action
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
text
,
button_text_style
.clone
(),
));
});
}
});
});
}
fn
display_settings_menu_setup
(
mut
commands
:
Commands
,
display_quality
:
Res
<
DisplayQuality
>
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnDisplaySettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Create a new `NodeBundle`, this time not setting its `flex_direction`. It will
// use the default value, `FlexDirection::Row`, from left to right.
parent
.spawn
(
NodeBundle
{
style
:
Style
{
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Display a label for the current setting
parent
.spawn
(
TextBundle
::
from_section
(
"Display Quality"
,
button_text_style
.clone
(),
));
// Display a button for each possible value
for
quality_setting
in
[
DisplayQuality
::
Low
,
DisplayQuality
::
Medium
,
DisplayQuality
::
High
,
]
{
let
mut
entity
=
parent
.spawn
(
ButtonBundle
{
style
:
Style
{
width
:
Val
::
Px
(
150.0
),
height
:
Val
::
Px
(
65.0
),
..
button_style
.clone
()
},
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
});
entity
.insert
(
quality_setting
)
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
format!
(
"{quality_setting:?}"
),
button_text_style
.clone
(),
));
});
if
*
display_quality
==
quality_setting
{
entity
.insert
(
SelectedOption
);
}
}
});
// Display the back button to return to the settings screen
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
BackToSettings
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Back"
,
button_text_style
));
});
});
});
}
fn
sound_settings_menu_setup
(
mut
commands
:
Commands
,
volume
:
Res
<
Volume
>
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnSoundSettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Volume"
,
button_text_style
.clone
(),
));
for
volume_setting
in
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
{
let
mut
entity
=
parent
.spawn
(
ButtonBundle
{
style
:
Style
{
width
:
Val
::
Px
(
30.0
),
height
:
Val
::
Px
(
65.0
),
..
button_style
.clone
()
},
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
});
entity
.insert
(
Volume
(
volume_setting
));
if
*
volume
==
Volume
(
volume_setting
)
{
entity
.insert
(
SelectedOption
);
}
}
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
BackToSettings
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Back"
,
button_text_style
));
});
});
});
}
fn
menu_action
(
interaction_query
:
Query
<
(
&
Interaction
,
&
MenuButtonAction
),
(
Changed
<
Interaction
>
,
With
<
Button
>
),
>
,
mut
app_exit_events
:
EventWriter
<
AppExit
>
,
mut
menu_state
:
ResMut
<
NextState
<
MenuState
>>
,
mut
game_state
:
ResMut
<
NextState
<
GameState
>>
,
)
{
for
(
interaction
,
menu_button_action
)
in
&
interaction_query
{
if
*
interaction
==
Interaction
::
Pressed
{
match
menu_button_action
{
MenuButtonAction
::
Quit
=>
app_exit_events
.send
(
AppExit
),
MenuButtonAction
::
Play
=>
{
game_state
.set
(
GameState
::
Game
);
menu_state
.set
(
MenuState
::
Disabled
);
}
MenuButtonAction
::
Settings
=>
menu_state
.set
(
MenuState
::
Settings
),
MenuButtonAction
::
SettingsDisplay
=>
{
menu_state
.set
(
MenuState
::
SettingsDisplay
);
}
MenuButtonAction
::
SettingsSound
=>
{
menu_state
.set
(
MenuState
::
SettingsSound
);
}
MenuButtonAction
::
BackToMainMenu
=>
menu_state
.set
(
MenuState
::
Main
),
MenuButtonAction
::
BackToSettings
=>
{
menu_state
.set
(
MenuState
::
Settings
);
}
}
}
}
}
}
// Generic system that takes a component as a parameter, and will despawn all entities with that component
// Generic system that takes a component as a parameter, and will despawn all entities with that component
fn
despawn_screen
<
T
:
Component
>
(
to_despawn
:
Query
<
Entity
,
With
<
T
>>
,
mut
commands
:
Commands
)
{
fn
despawn_screen
<
T
:
Component
>
(
to_despawn
:
Query
<
Entity
,
With
<
T
>>
,
mut
commands
:
Commands
)
{
...
...
This diff is collapsed.
Click to expand it.
game/src/menu.rs
0 → 100644
+
567
−
0
View file @
50ca7c78
use
bevy
::{
app
::
AppExit
,
prelude
::
*
};
use
super
::{
despawn_screen
,
DisplayQuality
,
GameState
,
Volume
,
TEXT_COLOR
};
// This plugin manages the menu, with 5 different screens:
// - a main menu with "New Game", "Settings", "Quit"
// - a settings menu with two submenus and a back button
// - two settings screen with a setting that can be set and a back button
pub
struct
MenuPlugin
;
impl
Plugin
for
MenuPlugin
{
fn
build
(
&
self
,
app
:
&
mut
App
)
{
app
// At start, the menu is not enabled. This will be changed in `menu_setup` when
// entering the `GameState::Menu` state.
// Current screen in the menu is handled by an independent state from `GameState`
.add_state
::
<
MenuState
>
()
.add_systems
(
OnEnter
(
GameState
::
Menu
),
menu_setup
)
// Systems to handle the main menu screen
.add_systems
(
OnEnter
(
MenuState
::
Main
),
main_menu_setup
)
.add_systems
(
OnExit
(
MenuState
::
Main
),
despawn_screen
::
<
OnMainMenuScreen
>
)
// Systems to handle the settings menu screen
.add_systems
(
OnEnter
(
MenuState
::
Settings
),
settings_menu_setup
)
.add_systems
(
OnExit
(
MenuState
::
Settings
),
despawn_screen
::
<
OnSettingsMenuScreen
>
,
)
// Systems to handle the display settings screen
.add_systems
(
OnEnter
(
MenuState
::
SettingsDisplay
),
display_settings_menu_setup
,
)
.add_systems
(
Update
,
(
setting_button
::
<
DisplayQuality
>
.run_if
(
in_state
(
MenuState
::
SettingsDisplay
)),
),
)
.add_systems
(
OnExit
(
MenuState
::
SettingsDisplay
),
despawn_screen
::
<
OnDisplaySettingsMenuScreen
>
,
)
// Systems to handle the sound settings screen
.add_systems
(
OnEnter
(
MenuState
::
SettingsSound
),
sound_settings_menu_setup
)
.add_systems
(
Update
,
setting_button
::
<
Volume
>
.run_if
(
in_state
(
MenuState
::
SettingsSound
)),
)
.add_systems
(
OnExit
(
MenuState
::
SettingsSound
),
despawn_screen
::
<
OnSoundSettingsMenuScreen
>
,
)
// Common systems to all screens that handles buttons behavior
.add_systems
(
Update
,
(
menu_action
,
button_system
)
.run_if
(
in_state
(
GameState
::
Menu
)),
);
}
}
// State used for the current menu screen
#[derive(Clone,
Copy,
Default,
Eq,
PartialEq,
Debug,
Hash,
States)]
enum
MenuState
{
Main
,
Settings
,
SettingsDisplay
,
SettingsSound
,
#[default]
Disabled
,
}
// Tag component used to tag entities added on the main menu screen
#[derive(Component)]
struct
OnMainMenuScreen
;
// Tag component used to tag entities added on the settings menu screen
#[derive(Component)]
struct
OnSettingsMenuScreen
;
// Tag component used to tag entities added on the display settings menu screen
#[derive(Component)]
struct
OnDisplaySettingsMenuScreen
;
// Tag component used to tag entities added on the sound settings menu screen
#[derive(Component)]
struct
OnSoundSettingsMenuScreen
;
const
NORMAL_BUTTON
:
Color
=
Color
::
rgb
(
0.15
,
0.15
,
0.15
);
const
HOVERED_BUTTON
:
Color
=
Color
::
rgb
(
0.25
,
0.25
,
0.25
);
const
HOVERED_PRESSED_BUTTON
:
Color
=
Color
::
rgb
(
0.25
,
0.65
,
0.25
);
const
PRESSED_BUTTON
:
Color
=
Color
::
rgb
(
0.35
,
0.75
,
0.35
);
// Tag component used to mark which setting is currently selected
#[derive(Component)]
struct
SelectedOption
;
// All actions that can be triggered from a button click
#[derive(Component)]
enum
MenuButtonAction
{
Play
,
Settings
,
SettingsDisplay
,
SettingsSound
,
BackToMainMenu
,
BackToSettings
,
Quit
,
}
// This system handles changing all buttons color based on mouse interaction
fn
button_system
(
mut
interaction_query
:
Query
<
(
&
Interaction
,
&
mut
BackgroundColor
,
Option
<&
SelectedOption
>
),
(
Changed
<
Interaction
>
,
With
<
Button
>
),
>
,
)
{
for
(
interaction
,
mut
color
,
selected
)
in
&
mut
interaction_query
{
*
color
=
match
(
*
interaction
,
selected
)
{
(
Interaction
::
Pressed
,
_
)
|
(
Interaction
::
None
,
Some
(
_
))
=>
PRESSED_BUTTON
.into
(),
(
Interaction
::
Hovered
,
Some
(
_
))
=>
HOVERED_PRESSED_BUTTON
.into
(),
(
Interaction
::
Hovered
,
None
)
=>
HOVERED_BUTTON
.into
(),
(
Interaction
::
None
,
None
)
=>
NORMAL_BUTTON
.into
(),
}
}
}
// This system updates the settings when a new value for a setting is selected, and marks
// the button as the one currently selected
fn
setting_button
<
T
:
Resource
+
Component
+
PartialEq
+
Copy
>
(
interaction_query
:
Query
<
(
&
Interaction
,
&
T
,
Entity
),
(
Changed
<
Interaction
>
,
With
<
Button
>
)
>
,
mut
selected_query
:
Query
<
(
Entity
,
&
mut
BackgroundColor
),
With
<
SelectedOption
>>
,
mut
commands
:
Commands
,
mut
setting
:
ResMut
<
T
>
,
)
{
for
(
interaction
,
button_setting
,
entity
)
in
&
interaction_query
{
if
*
interaction
==
Interaction
::
Pressed
&&
*
setting
!=
*
button_setting
{
let
(
previous_button
,
mut
previous_color
)
=
selected_query
.single_mut
();
*
previous_color
=
NORMAL_BUTTON
.into
();
commands
.entity
(
previous_button
)
.remove
::
<
SelectedOption
>
();
commands
.entity
(
entity
)
.insert
(
SelectedOption
);
*
setting
=
*
button_setting
;
}
}
}
fn
menu_setup
(
mut
menu_state
:
ResMut
<
NextState
<
MenuState
>>
)
{
menu_state
.set
(
MenuState
::
Main
);
}
fn
main_menu_setup
(
mut
commands
:
Commands
,
asset_server
:
Res
<
AssetServer
>
)
{
// Common style for all buttons on the screen
let
button_style
=
Style
{
width
:
Val
::
Px
(
250.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_icon_style
=
Style
{
width
:
Val
::
Px
(
30.0
),
// This takes the icons out of the flexbox flow, to be positioned exactly
position_type
:
PositionType
::
Absolute
,
// The icon will be close to the left border of the button
left
:
Val
::
Px
(
10.0
),
right
:
Val
::
Auto
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnMainMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Display the game name
parent
.spawn
(
TextBundle
::
from_section
(
"Poke Profs
\n
Menu"
,
TextStyle
{
font_size
:
80.0
,
color
:
TEXT_COLOR
,
..
default
()
},
)
.with_style
(
Style
{
margin
:
UiRect
::
all
(
Val
::
Px
(
50.0
)),
..
default
()
}),
);
// Display three buttons for each action available from the main menu:
// - new game
// - settings
// - quit
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Play
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/right.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
.clone
(),
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"New Game"
,
button_text_style
.clone
(),
));
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Settings
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/wrench.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
.clone
(),
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"Settings"
,
button_text_style
.clone
(),
));
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
Quit
,
))
.with_children
(|
parent
|
{
let
icon
=
asset_server
.load
(
"textures/Game Icons/exitRight.png"
);
parent
.spawn
(
ImageBundle
{
style
:
button_icon_style
,
image
:
UiImage
::
new
(
icon
),
..
default
()
});
parent
.spawn
(
TextBundle
::
from_section
(
"Quit"
,
button_text_style
));
});
});
});
}
fn
settings_menu_setup
(
mut
commands
:
Commands
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnSettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
for
(
action
,
text
)
in
[
(
MenuButtonAction
::
SettingsDisplay
,
"Display"
),
(
MenuButtonAction
::
SettingsSound
,
"Sound"
),
(
MenuButtonAction
::
BackToMainMenu
,
"Back"
),
]
{
parent
.spawn
((
ButtonBundle
{
style
:
button_style
.clone
(),
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
action
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
text
,
button_text_style
.clone
(),
));
});
}
});
});
}
fn
display_settings_menu_setup
(
mut
commands
:
Commands
,
display_quality
:
Res
<
DisplayQuality
>
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnDisplaySettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Create a new `NodeBundle`, this time not setting its `flex_direction`. It will
// use the default value, `FlexDirection::Row`, from left to right.
parent
.spawn
(
NodeBundle
{
style
:
Style
{
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
// Display a label for the current setting
parent
.spawn
(
TextBundle
::
from_section
(
"Display Quality"
,
button_text_style
.clone
(),
));
// Display a button for each possible value
for
quality_setting
in
[
DisplayQuality
::
Low
,
DisplayQuality
::
Medium
,
DisplayQuality
::
High
,
]
{
let
mut
entity
=
parent
.spawn
(
ButtonBundle
{
style
:
Style
{
width
:
Val
::
Px
(
150.0
),
height
:
Val
::
Px
(
65.0
),
..
button_style
.clone
()
},
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
});
entity
.insert
(
quality_setting
)
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
format!
(
"{quality_setting:?}"
),
button_text_style
.clone
(),
));
});
if
*
display_quality
==
quality_setting
{
entity
.insert
(
SelectedOption
);
}
}
});
// Display the back button to return to the settings screen
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
BackToSettings
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Back"
,
button_text_style
));
});
});
});
}
fn
sound_settings_menu_setup
(
mut
commands
:
Commands
,
volume
:
Res
<
Volume
>
)
{
let
button_style
=
Style
{
width
:
Val
::
Px
(
200.0
),
height
:
Val
::
Px
(
65.0
),
margin
:
UiRect
::
all
(
Val
::
Px
(
20.0
)),
justify_content
:
JustifyContent
::
Center
,
align_items
:
AlignItems
::
Center
,
..
default
()
};
let
button_text_style
=
TextStyle
{
font_size
:
40.0
,
color
:
TEXT_COLOR
,
..
default
()
};
commands
.spawn
((
NodeBundle
{
style
:
Style
{
width
:
Val
::
Percent
(
100.0
),
align_items
:
AlignItems
::
Center
,
justify_content
:
JustifyContent
::
Center
,
..
default
()
},
..
default
()
},
OnSoundSettingsMenuScreen
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
flex_direction
:
FlexDirection
::
Column
,
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
parent
.spawn
(
NodeBundle
{
style
:
Style
{
align_items
:
AlignItems
::
Center
,
..
default
()
},
background_color
:
Color
::
BLACK
.into
(),
..
default
()
})
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Volume"
,
button_text_style
.clone
(),
));
for
volume_setting
in
[
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
]
{
let
mut
entity
=
parent
.spawn
(
ButtonBundle
{
style
:
Style
{
width
:
Val
::
Px
(
30.0
),
height
:
Val
::
Px
(
65.0
),
..
button_style
.clone
()
},
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
});
entity
.insert
(
Volume
(
volume_setting
));
if
*
volume
==
Volume
(
volume_setting
)
{
entity
.insert
(
SelectedOption
);
}
}
});
parent
.spawn
((
ButtonBundle
{
style
:
button_style
,
background_color
:
NORMAL_BUTTON
.into
(),
..
default
()
},
MenuButtonAction
::
BackToSettings
,
))
.with_children
(|
parent
|
{
parent
.spawn
(
TextBundle
::
from_section
(
"Back"
,
button_text_style
));
});
});
});
}
fn
menu_action
(
interaction_query
:
Query
<
(
&
Interaction
,
&
MenuButtonAction
),
(
Changed
<
Interaction
>
,
With
<
Button
>
),
>
,
mut
app_exit_events
:
EventWriter
<
AppExit
>
,
mut
menu_state
:
ResMut
<
NextState
<
MenuState
>>
,
mut
game_state
:
ResMut
<
NextState
<
GameState
>>
,
)
{
for
(
interaction
,
menu_button_action
)
in
&
interaction_query
{
if
*
interaction
==
Interaction
::
Pressed
{
match
menu_button_action
{
MenuButtonAction
::
Quit
=>
app_exit_events
.send
(
AppExit
),
MenuButtonAction
::
Play
=>
{
game_state
.set
(
GameState
::
Game
);
menu_state
.set
(
MenuState
::
Disabled
);
}
MenuButtonAction
::
Settings
=>
menu_state
.set
(
MenuState
::
Settings
),
MenuButtonAction
::
SettingsDisplay
=>
{
menu_state
.set
(
MenuState
::
SettingsDisplay
);
}
MenuButtonAction
::
SettingsSound
=>
{
menu_state
.set
(
MenuState
::
SettingsSound
);
}
MenuButtonAction
::
BackToMainMenu
=>
menu_state
.set
(
MenuState
::
Main
),
MenuButtonAction
::
BackToSettings
=>
{
menu_state
.set
(
MenuState
::
Settings
);
}
}
}
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment