-
-
Save westonsoftware/a3fa982397fe1817ece4a27d3cbc5a89 to your computer and use it in GitHub Desktop.
| using System; | |
| using Stride.Core.Mathematics; | |
| using Stride.Engine; | |
| using Stride.Rendering; | |
| using Stride.Graphics; | |
| using System.IO; | |
| namespace InfinityGame | |
| { | |
| public class StartScript : SyncScript | |
| { | |
| private Random random = new Random(); | |
| public override void Start() | |
| { | |
| var model = Content.Load<Model>("Sphere"); | |
| var size = 100; | |
| var count = 1000; | |
| for (int i = 0; i < count; i++) | |
| { | |
| var modelComponent = new ModelComponent(model); | |
| var randomPosition = new Vector3(random.Next(-size, size), random.Next(0, size), random.Next(-size, size)); | |
| var entity = new Entity(randomPosition, "NEW"); | |
| Entity.Scene.Entities.Add(entity); | |
| entity.Add(modelComponent); | |
| } | |
| Game.Window.Position = new Int2(0, 0); | |
| } | |
| public override void Update() | |
| { | |
| var sprite = Entity.Get<SpriteComponent>(); | |
| var texture = sprite.CurrentSprite.Texture; | |
| using (var image = texture.GetDataAsImage(Game.GraphicsContext.CommandList)) | |
| { | |
| using (var stream = new MemoryStream()) | |
| { | |
| image.Save(stream, ImageFileType.Bmp); | |
| GlobalEvents.FrameReadyEventKey.Broadcast(stream.ToArray()); | |
| } | |
| } | |
| } | |
| } | |
| } |
| using Avalonia.Controls; | |
| using Avalonia.Markup.Xaml; | |
| using InfinityGame; | |
| using Stride.Engine.Events; | |
| using System; | |
| using System.IO; | |
| using System.Windows.Threading; | |
| namespace AvaloniaApplication1.Views | |
| { | |
| public class UserControl1 : UserControl | |
| { | |
| private readonly DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) }; | |
| public UserControl1() | |
| { | |
| this.InitializeComponent(); | |
| } | |
| private void InitializeComponent() | |
| { | |
| AvaloniaXamlLoader.Load(this); | |
| var strideImage = this.Find<Image>("StrideView"); | |
| var frameReady = new EventReceiver<byte[]>(GlobalEvents.FrameReadyEventKey); | |
| timer.Tick += (s,e) => | |
| { | |
| if (frameReady.TryReceive(out byte[] bmp)) | |
| { | |
| using var stream = new MemoryStream(bmp); | |
| var bitmap = new Avalonia.Media.Imaging.Bitmap(stream); | |
| strideImage.Source = bitmap; | |
| } | |
| }; | |
| timer.Start(); | |
| } | |
| } | |
| } |
Hi @westonsoftware @tebjan
I found this gist in Stride community resources, looking for AvaloniaUI integration.
I would like to run a Stride game inside an Avalonia app.
It seems to me that this gist just displays a texture inside an Avalonia image. Is that correct?
Is there a way to render a whole game, like it is documented for WPF?
Hi @luca-domenichini
You can try the same approach as in the example for WPF. Example:
StrideGameHost.cs
`using Avalonia.Controls;
using Avalonia.Platform;
using Stride.CommunityToolkit.Engine;
using Stride.Core.Diagnostics;
using Stride.Engine;
using Stride.Games;
using Stride.CommunityToolkit.Bepu;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Stride.Avalonia.Controls
{
public class StrideGameHost : NativeControlHost, IDisposable
{
private Thread? gameThread;
private readonly TaskCompletionSource gameStartedTaskSource = new TaskCompletionSource();
private IntPtr windowHandle;
private Game? game;
private bool gameStarted = false;
public StrideGameHost() : base()
{
if(Design.IsDesignMode)
{
return;
}
this.gameThread = new Thread(SafeAction.Wrap(GameRunThread))
{
IsBackground = true,
Name = "Game Thread"
};
if(OperatingSystem.IsWindows())
{
this.gameThread.SetApartmentState(ApartmentState.STA);
}
this.SizeChanged += (s, e) =>
{
if (this.game != null && this.game.Window != null)
{
this.game.Window
.SetSize(new Core.Mathematics.Int2((int)this.Bounds.Width, (int)this.Bounds.Height));
}
};
}
protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent)
{
if (OperatingSystem.IsWindows() && this.gameThread != null)
{
this.gameThread.Start();
this.gameStartedTaskSource.Task
.GetAwaiter()
.GetResult();
return new PlatformHandle(this.windowHandle, "HWND");
}
return base.CreateNativeControlCore(parent);
}
public void Dispose()
{
if (this.game != null)
{
this.game.Dispose();
this.game = null;
}
}
protected virtual void StartGame(Scene scene)
{
if (this.game == null)
{
return;
}
this.game.Window.IsBorderLess = true;
this.game.SetupBase3DScene();
}
protected virtual void UpdateGame(Scene scene, GameTime gameTime)
{
if (this.game == null)
{
return;
}
if(!this.gameStarted)
{
this.gameStartedTaskSource.SetResult(true);
this.gameStarted = true;
this.windowHandle = this.game.Window.NativeWindow.Handle;
}
}
private void GameRunThread()
{
this.game = new Game();
this.game.Run(
StartGame,
UpdateGame);
}
}
}
`
MainView.axml
<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:AvaloniaStrideEmbedding.ViewModels" xmlns:st="clr-namespace:Stride.Avalonia.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="AvaloniaStrideEmbedding.Views.MainView" x:DataType="vm:MainViewModel" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Design.DataContext> <!-- This only sets the DataContext for the previewer in an IDE, to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) --> <vm:MainViewModel /> </Design.DataContext> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap"/> <st:StrideGameHost Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" /> </Grid> </UserControl>
If anyone is looking for the
GlobalEvents.FrameReadyEventKeyyou simply add it to your global events file: https://doc.stride3d.net/4.2/en/manual/scripts/events.htmlFor example:
Also, note that downloading a texture from the GPU is a slow and blocking operation. Stride has a better
TextureReadbackclass that at least doesn't block. However, the correct solution would be to create AvaloniaUI with a GPU context, if possible, and share a texture via a shared handle. This would be possible with Angle: AvaloniaUI/Avalonia#5432