1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Merge pull request #111296 from syntaxerror247/speed-control

Android Editor: Add game speed control options in game menu bar
This commit is contained in:
Thaddeus Crews
2025-10-21 10:27:02 -05:00
9 changed files with 155 additions and 0 deletions

View File

@@ -142,4 +142,22 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_set
}
#endif
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetTimeScale(JNIEnv *env, jclass clazz) {
#ifdef TOOLS_ENABLED
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
game_view_plugin->get_debugger()->reset_time_scale();
}
#endif
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setTimeScale(JNIEnv *env, jclass clazz, jdouble scale) {
#ifdef TOOLS_ENABLED
GameViewPlugin *game_view_plugin = _get_game_view_plugin();
if (game_view_plugin != nullptr && game_view_plugin->get_debugger().is_valid()) {
game_view_plugin->get_debugger()->set_time_scale(scale);
}
#endif
}
}

View File

@@ -44,4 +44,6 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_res
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetCamera3DPosition(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_playMainScene(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setDebugMuteAudio(JNIEnv *env, jclass clazz, jboolean enabled);
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_resetTimeScale(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_editor_utils_GameMenuUtils_setTimeScale(JNIEnv *env, jclass clazz, jdouble scale);
}

View File

@@ -150,6 +150,8 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
internal const val GAME_MENU_ACTION_RESET_CAMERA_3D_POSITION = "resetCamera3DPosition"
internal const val GAME_MENU_ACTION_EMBED_GAME_ON_PLAY = "embedGameOnPlay"
internal const val GAME_MENU_ACTION_SET_DEBUG_MUTE_AUDIO = "setDebugMuteAudio"
internal const val GAME_MENU_ACTION_RESET_TIME_SCALE = "resetTimeScale"
internal const val GAME_MENU_ACTION_SET_TIME_SCALE = "setTimeScale"
private const val GAME_WORKSPACE = "Game"
@@ -839,6 +841,13 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
val enabled = actionData.getBoolean(KEY_GAME_MENU_ACTION_PARAM1)
muteAudio(enabled)
}
GAME_MENU_ACTION_RESET_TIME_SCALE -> {
resetTimeScale()
}
GAME_MENU_ACTION_SET_TIME_SCALE -> {
val scale = actionData.getDouble(KEY_GAME_MENU_ACTION_PARAM1)
setTimeScale(scale)
}
}
}
@@ -909,6 +918,20 @@ abstract class BaseGodotEditor : GodotActivity(), GameMenuFragment.GameMenuListe
}
}
override fun resetTimeScale() {
gameMenuState.putDouble(GAME_MENU_ACTION_SET_TIME_SCALE, 1.0)
godot?.runOnRenderThread {
GameMenuUtils.resetTimeScale()
}
}
override fun setTimeScale(scale: Double) {
gameMenuState.putDouble(GAME_MENU_ACTION_SET_TIME_SCALE, scale)
godot?.runOnRenderThread {
GameMenuUtils.setTimeScale(scale)
}
}
override fun embedGameOnPlay(embedded: Boolean) {
gameMenuState.putBoolean(GAME_MENU_ACTION_EMBED_GAME_ON_PLAY, embedded)
godot?.runOnRenderThread {

View File

@@ -230,6 +230,21 @@ open class GodotGame : BaseGodotGame() {
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
}
override fun resetTimeScale() {
val actionBundle = Bundle().apply {
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_RESET_TIME_SCALE)
}
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
}
override fun setTimeScale(scale: Double) {
val actionBundle = Bundle().apply {
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_SET_TIME_SCALE)
putDouble(KEY_GAME_MENU_ACTION_PARAM1, scale)
}
editorMessageDispatcher.dispatchGameMenuAction(EDITOR_MAIN_INFO, actionBundle)
}
override fun embedGameOnPlay(embedded: Boolean) {
val actionBundle = Bundle().apply {
putString(KEY_GAME_MENU_ACTION, GAME_MENU_ACTION_EMBED_GAME_ON_PLAY)

View File

@@ -38,6 +38,7 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.PopupMenu
import android.widget.RadioButton
import androidx.core.content.edit
@@ -102,6 +103,8 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
fun reset3DCamera()
fun manipulateCamera(mode: CameraMode)
fun muteAudio(enabled: Boolean)
fun resetTimeScale()
fun setTimeScale(scale: Double)
fun isGameEmbeddingSupported(): Boolean
fun embedGameOnPlay(embedded: Boolean)
@@ -131,6 +134,12 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
private val nextFrameButton: View? by lazy {
view?.findViewById(R.id.game_menu_next_frame_button)
}
private val setTimeScaleButton: Button? by lazy {
view?.findViewById<Button>(R.id.game_menu_set_time_scale_button)
}
private val resetTimeScaleButton: View? by lazy {
view?.findViewById(R.id.game_menu_reset_time_scale_button)
}
private val unselectNodesButton: RadioButton? by lazy {
view?.findViewById(R.id.game_menu_unselect_nodes_button)
}
@@ -179,6 +188,33 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
}
}
private val timeScaleMenu: PopupMenu by lazy {
PopupMenu(context, setTimeScaleButton).apply {
inflate(R.menu.time_scale_options)
setOnMenuItemClickListener { menuItem: MenuItem ->
val selectedScale = when (menuItem.itemId) {
R.id.speed_1_16 -> 0.0625f
R.id.speed_1_8 -> 0.125f
R.id.speed_1_4 -> 0.25f
R.id.speed_1_2 -> 0.5f
R.id.speed_3_4 -> 0.75f
R.id.speed_1_0 -> 1.0f
R.id.speed_1_25 -> 1.25f
R.id.speed_1_5 -> 1.5f
R.id.speed_1_75 -> 1.75f
R.id.speed_2_0 -> 2.0f
R.id.speed_4_0 -> 4.0f
R.id.speed_8_0 -> 8.0f
R.id.speed_16_0 -> 16.0f
else -> 1.0f
}
setTimeScaleButton?.text = menuItem.title
menuListener?.setTimeScale(selectedScale.toDouble())
true
}
}
}
private val menuItemActionView: View by lazy {
View(context)
}
@@ -280,6 +316,19 @@ class GameMenuFragment : Fragment(), PopupMenu.OnMenuItemClickListener {
}
}
setTimeScaleButton?.apply {
setOnClickListener {
timeScaleMenu.show()
}
}
resetTimeScaleButton?.apply {
setOnClickListener {
menuListener?.resetTimeScale()
setTimeScaleButton?.text = "1.0x"
}
}
unselectNodesButton?.apply{
setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M5,8a4,4 0,1 1,4 4v2a6,6 0,1 0,-6 -6H1l3,4 3,-4z"
android:fillColor="@color/game_menu_icons_color_state"/>
</vector>

View File

@@ -33,6 +33,22 @@
android:background="@drawable/game_menu_button_bg"
android:src="@drawable/next_frame" />
<Button
android:id="@+id/game_menu_set_time_scale_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/game_menu_button_bg"
android:text="1.0x"/>
<ImageButton
android:id="@+id/game_menu_reset_time_scale_button"
style="?android:attr/borderlessButtonStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/game_menu_button_bg"
android:src="@drawable/reset" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/speed_1_16" android:title="1/16x" />
<item android:id="@+id/speed_1_8" android:title="1/8x" />
<item android:id="@+id/speed_1_4" android:title="1/4x" />
<item android:id="@+id/speed_1_2" android:title="1/2x" />
<item android:id="@+id/speed_3_4" android:title="3/4x" />
<item android:id="@+id/speed_1_0" android:title="1.0x" />
<item android:id="@+id/speed_1_25" android:title="1.25x" />
<item android:id="@+id/speed_1_5" android:title="1.5x" />
<item android:id="@+id/speed_1_75" android:title="1.75x" />
<item android:id="@+id/speed_2_0" android:title="2.0x" />
<item android:id="@+id/speed_4_0" android:title="4.0x" />
<item android:id="@+id/speed_8_0" android:title="8.0x" />
<item android:id="@+id/speed_16_0" android:title="16.0x" />
</menu>

View File

@@ -95,6 +95,13 @@ object GameMenuUtils {
@JvmStatic
external fun setDebugMuteAudio(enabled: Boolean)
@JvmStatic
external fun resetTimeScale()
@JvmStatic
external fun setTimeScale(scale: Double)
/**
* Returns [GameEmbedMode] stored in the editor settings.
*