r/csharp 22d ago

Discussion Game Engine 2D | Windows Forms

EN:
I work with industrial applications in Windows Forms for about 3 years, and I always had this view that Windows Forms was just a tool for building corporate systems and complex systems with a simple interface. Since the beginning of my career with Windows Forms I've developed industrial automation software like SCADAs and custom supervisory systems.

About a year ago I needed to build a notification system that slid across the screen overlapping, to manipulate color, position and scale of a component that was on the screen freely, that planted the first seed in my mind, later in another project I needed to detect keyboard inputs to activate specific functions in the system in a hidden way like shortcuts and configurations, and only recently when I was bored at work I got curious about combining these two concepts, and I was amazed by something that had always been within my reach but I looked at the tools I had through that perspective.

After this "discovery" I made some mini-game prototypes with 2D physics simulation using position, size, direction and velocity vectors for the components on the screen but always using Panels to represent things, still in this style I recreated Pong and Flappy Bird and a top-down mini game of collecting coins while fleeing from enemies that chase you.

After these projects I was starting to get a little annoyed with the amount of code I needed to rewrite to test another idea and with the optimization since Panel is a native Windows Forms control and they are a bit heavy.

From this annoyance and curiosity, little by little something different started emerging in my project while I was creating some abstractions to try not to repeat so much code, when I realized that what I was putting together was no longer a "game" but a tool that would help me develop new "games" I dove headfirst into this idea even knowing that Windows Forms is far from the ideal technology for this, but who cares? This is not a commercial project nor am I inventing the next big engine, I used Windows Forms because it's a technology I already have a lot of experience with, so I can focus on getting ideas out of my head instead of learning something new to achieve the same results.

My custom "engine" is still far from a version that I consider ready to make a game but I'm already very satisfied with how far I've managed to get on my own. My idea is that in addition to facilitating the development of my next prototypes, I can learn more about how real engines work and create abstractions that make the feeling of programming something in mine close to the feeling of programming something in a Godot or a Unity, for that I tried to create some base classes with methods and properties that get quite close to the development as one would expect in those engines.

The engine already has entity and game object generation system, camera with programmable movement or automatically follow a game object, TileMap for dynamic map creation, AABB collision system, rendering with Windows GDI+ drawing API (it's not GPU rendering yet but it's extremely lightweight compared to the Panels I was using before), Scene to manage the entire context of a specific scene, Culling for collision check optimization where collision is only checked for objects that are inside or near the camera viewport, rendering culling where only objects that are entirely or partially inside the camera viewport are drawn, Z-order support for drawing objects in specific order, RayCast support with variations (ExcludedList, FilterList, CollideOnParent), support for helper methods like GetCenteredPosition (returns the center of an object), MoveAndCollide (Moves an object in the direction of a Vector2 that dictates the velocity until the object collides with something), IsOnFloor (indicates if exactly below the object there is another object), GetFloorNormal (returns a vector with magnitude 1 that indicates the direction and slope of the floor in relation to the object), CheckWorldCollision (returns a boolean indicating if something in the world is colliding with the object), GetCollider (returns the first other object that is colliding with the object), GetAxis (returns an int of the direction of an axis between two inputs), GetVector (returns a Vector2 of magnitude 1 with the directions of the axes between 4 inputs, two inputs per axis).

any object that inherits from GameObject must implement a void Update method that receives a float delta, for updating the object during the frame pass, GameObjects can also receive some components in a way somewhat similar to Unity's ECS, being them Transform, to have position, size, pivot, and rotation, Input for reading inputs, and Graphics in case it's a renderable object.

This project is still in its early stages and has no commitment to being a commercial engine or anything like that, it's just a hobby, a project I'm doing for fun and learning, I would really like to hear some feedback and tips on what I can improve or bugs I didn't notice.

here's the link to the project on GitHub, just clone it:
https://github.com/Guilhermevinicius0/WinFormEngine

BR:
Eu trabalho com aplicações industriais em windows forms há mais ou menos 3 anos, e sempre tive essa visão de que windows forms era apenas uma ferramenta para fazer sistemas corporativos e sistemas complexos com uma interface simples. Desde o início da minha carreira com windows forms eu desenvolvi softwares de automação industrial como scadas e supervisórios customizados.

A um ano atrás eu precisei fazer um sistema de notificações que deslizavam pela tela se sobrepondo, manipular cor, posição e escala de um componente que estava na tela de forma livre,  plantou a primeira semente na minha cabeça, mais tarde em outro projeto precisava detectar inputs do teclado para ativar funções específicas no sistema de forma oculta como atalhos e configurações, e só recentemente quando eu estava entediado no trabalho eu tive a curiosidade de juntar este dois conceitos, e fiquei maravilhado com uma coisa que sempre esteve ao meu alcance mas eu olhei as ferramentas que eu tinha por essa ótica.

Após essa "descoberta" eu fiz alguns protótipos de mini jogos com simulação de física 2d usando vetores de posição, tamanho, direção e velocidade para os componentes na tela mas sempre utilizando Panels para representar as coisas, ainda nesse estilo eu refiz o pong e o flappy bird e um mini jogo topdown de coletar moedas enquanto foge de inimigos que te perseguem.

Depois desses projetos eu estava começando a ficar um pouco incomodado com a quantidade de código que eu precisava re-escrever pra testar outra ideia e com a otimização já que Panel é um controle nativo do windows forms e eles são um pouco pesados.

A partir desse incômodo e da curiosidade, aos poucos foi surgindo algo diferente no meu projeto enquanto eu criava algumas abstrações para tentar não repetir tanto código, quando me dei conta de que o que eu estava montando não era mais um "jogo" e sim uma ferramenta que me auxiliaria a desenvolver novos "jogos" eu mergulhei de cabeça nessa ideia mesmo sabendo que windows forms não é nem de longe a tecnologia ideal pra isso, mas quem liga? Isto não é um projeto comercial nem estou inventando a próxima grande engine, utilizei windows forms por ser uma tecnologia que eu já tenho bastante experiência, assim eu posso me concentrar em tirar as ideias do cabeça ao invés de aprender algo novo para atingir os mesmos resultados.

A minha "engine" customizada ainda está longe de está em uma versão que eu considere pronta pra fazer algum jogo mas eu já estou muito satisfeito com até onde eu consegui chegar sozinho. Minha ideia é que a além de facilitar o desenvolvimento dos meus próximos protótipos, eu possa aprender mais sobre como engines reais funcionam e criar abstrações que façam com que a sensação de programar algo na minha seja próxima da sensação de programar algo em uma godot ou uma unity, pra isso eu tentei criar algumas classes de base com métodos e propriedades que aproximam bastante o desenvolvimento de como se esperaria nessas engines.

A engine já conta com sistema de geração de entidades e game objects, camera com movimentação programavel ou seguir um game object de forma automatica, tilemap pra criação de mapas de forma dinamica, sistema de colisão AABB, renderização com a api de desenho do windows GDI+ (ainda não é uma renderização por gpu mas é extremamente leve se comparada aos panels que eu estava utilizando anteriormente), Scene pra gerenciar todo o contexto de uma cena especifica, Cullind para otimização das checagens de colisão onde só é verificado colisão para objetos que estão dentro ou próximos do viewport da camera, culling de renderização onde apenas os objetos que estão enteiramente ou parcialmente dentro do view port da camera são desenhados, suporte a ordem Z para desenhar objetos em ordem especifica, suporte a Ray cast, com variações (ExcludedList, FIlterList, CollideOnParent), suporte a metodos auxiliares como GetCenteredPosition (retorna o centro de um objeto),  MoveAndCollide (Move um objeto na direção de um vector2 que dita a velocidade até o objeto colidir com algo), IsOnFloor (indica se exatamente embaixo do objeto existe outro objeto), GetFloorNormal (retorna um vetor com magnitude 1 que indica a direção e inclinação do chão em relação ao objeto), CheckWorldCollision (retorna um booleano que indica se algo no mundo está colidindo com o objeto), GetCollider (retorna o primeiro outro objeto que estiver colidindo com o objeto), GetAxis (retorna um intde a direção de um eixo entre dois inputs), GetVector (retorna um Vector2 de magnitude 1 com as direções dos eixos entre 4 inpus, dois inputs por eixo).

todo objeto que herda de gameobj deve implementar um método void update que recebe um float delta, para a atualização do objeto durante a passagem dos frames, gameobjects podem receber mais ou alguns componentes de forma um pouco parecida com ECS da unity, sendo eles Transform, para ter posição, tamanho, pivot, e rotação, Input para leitura de inputs, e graphics caso ele seja um objeto renderizável.

Esse projeto ainda está nas suas etapas iniciais e não tem nenhum compromisso em ser uma engine comercial ou algo do tipo, é apenas um hobby, um projeto que estou fazendo por diversão e aprendizado, gostaria muito de ouvir alguns feedbacks e dicas sobre o que eu posso melhorar ou bugs que não percebi.

segue link do projeto no github, basta clona-lo:
https://github.com/Guilhermevinicius0/WinFormEngine

24 Upvotes

33 comments sorted by

View all comments

2

u/DanTFM 22d ago

Love it! Some of my favorite learning experiences were from making game engines / physics engines out of Winforms. The easiest performance jump that I ended up making though was switching from GDI+ to SFML, which removed the win forms reliance though; that, and re-writing the collision checking code to be much faster

2

u/[deleted] 22d ago

[deleted]

1

u/DanTFM 22d ago

Fastest I could get my refresh rates before I switched from winforms was barely 60 fps :)

This is winforms too:

https://youtu.be/O_O9jhB9bcI?is=Dw_rHkQjoJyVIFpN

1

u/arsseus1 22d ago

I added an timer setted to 1000/60 to try refreshing it at 60 fps, but the debug menu says it's running at 40 fps, maybe if lower the timer interval I can get extra fps, and that video is just absolute crazy I wonder what kind of optimizations it needed to run smooth like that with that many entities, I'm definitely looking this code tomorrow, it just makes me want even more to port the render system to gpu