diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..964d327 --- /dev/null +++ b/LICENCE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Lucas Kalil + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/gifs/example1.gif b/gifs/example1.gif new file mode 100644 index 0000000..09cec77 Binary files /dev/null and b/gifs/example1.gif differ diff --git a/gifs/python+numpy+numba.gif b/gifs/python+numpy+numba.gif new file mode 100644 index 0000000..8c8bda0 Binary files /dev/null and b/gifs/python+numpy+numba.gif differ diff --git a/gifs/python+numpy.gif b/gifs/python+numpy.gif new file mode 100644 index 0000000..3bd6920 Binary files /dev/null and b/gifs/python+numpy.gif differ diff --git a/readme.md b/readme.md index e69de29..feee4eb 100644 --- a/readme.md +++ b/readme.md @@ -0,0 +1,137 @@ +# Sand box Python + +Este projeto foi desenvolvido em pouco mais de 2 dias durante um fim de semana. A principal motivação para sua criação foi a nostalgia de um jogo de navegador da minha infância, o [Powder Game](https://dan-ball.jp/en/javagame/dust/). O objetivo foi criar algo semelhante ao jogo original, embora, no meu projeto, existam apenas 6 tipos diferentes de elementos, em contraste com a grande variedade do jogo original. + +# Índice + +1. [Sand Box Python](#sand-box-python) +2. [Dificuldades e Como As Contornei](#dificuldades-e-como-as-contornei) + 1. [Problemas](#problemas) + 2. [Solução Parcial](#solução-parcial) + 3. [Solução Final](#solução-final) +3. [Como Executar e Instalar](#como-executar-e-instalar) +4. [Configurando Propriedades](#configurando-propriedades) +5. [Elementos](#elementos-e-comandos) +6. [Contribuições](#contribuições) +7. [Licença](#licença) +8. [Contato](#contato) + +# Dificuldades e Como As Contornei + +A ideia original era desenvolver o jogo em uma engine como Unity ou Unreal. No entanto, devido ao tempo limitado, optei por usar uma tecnologia com a qual já tinha familiaridade: Python. + +## Problemas + +O principal problema surgiu ao tentar iniciar o jogo com [Python](https://www.python.org/) puro, usando arrays nativos para uma tela de 500x500 pixels, resultando em um array com 250.000 itens. Percorrer cada item a cada quadro resultava em um desempenho muito ruim, com apenas 1 a 5 fps sem mencionar o uso de memória, que era gigantesco. + +## Solução Parcial + +Para melhorar a performance, substituí o array nativo por um array do [NumPy](https://numpy.org/), que oferece vantagens significativas em termos de desempenho e uso de memória. Cada pixel foi representado por 5 números `uint8`, o que resultou em uma melhoria modesta, alcançando 5 a 10 fps, mas ainda não o suficiente para uma jogabilidade aceitável. + +### por que 5 numeros? + +```python + class Types(Enum): + BG = (0, 0, 0, 0, 0) + SAND = (203, 189, 147, 1, 0) + WATER = (28, 163, 236, 2, 0) + STONE = (115, 112, 112, 3, 0) + VACUUM = (20, 20, 20, 4, 0) + CLONER = (108, 60, 12, 5, 0) +``` + +- Os três primeiros números representam a cor do pixel, permitindo leves variações dentro do mesmo elemento. +- O quarto valor é o ID do pixel, por exemplo, o ID da água é 2. +- O último valor é para dados adicionais, utilizado principalmente pelo elemento "cloner", mas pode ser usado para outros tipos de elementos, se necessário. + +Dessa forma, cada pixel na minha tela é representado por 5 números uint8, totalizando 5 bytes por pixel. Para um array de 250.000 pixels, isso resulta em 1.250.000 bytes ou 1,25 MB, um valor baixo e eficiente. + +#### python + numpy 320 x 320 exemplo AVG: 7 fps + +![python and numpy example](./gifs/python+numpy.gif) + +## Solução Final + +Recentemente, descobri a possibilidade de compilar código em tempo de execução (JIT) usando [Numba](https://numba.pydata.org/). Embora o Numba tenha algumas limitações quanto aos tipos de dados que pode manipular, consegui adaptar o código para utilizá-lo. Isso resultou em um aumento drástico na performance, elevando a taxa de quadros para 150 a 200 fps, tornando o jogo jogável. + +#### python + numpy + numba 1000 x 1000 exemplo AVG: 120 fps + +![python and numpy example](./gifs/python+numpy+numba.gif) + +# Como Executar e Instalar + +```bash + + # Clone o repositório + git clone https://github.com/LucasKalil-Programador/sand-box-python.git + + # Acesse a pasta do projeto + cd sand-box-python + + # Crie um novo ambiente virtual + python -m venv .venv + + # Ative o ambiente virtual + # No Windows: + .venv\Scripts\activate + # No macOS/Linux: + source .venv/bin/activate + + # Atualize o pip para a versão mais recente + python -m pip install --upgrade pip + + # Instale as dependências do projeto + pip install . + + # Execute o código + python main.py +``` + +# configurando propiedades + +Para ajustar as configurações básicas do jogo, abra o arquivo [main.py](./main.py). No final do arquivo, você encontrará a instância da classe Game, onde é possível modificar as propriedades. Veja o exemplo abaixo: + +```python + game = Game(size=(160, 160), fps=120, scale=4, font_size=30, test_mode=False) + game.run() +``` + +- `size`: Define o tamanho da tela como uma tupla (largura, altura). +- `scale`: Define o multiplicador do tamanho da janela. Por exemplo, com size=(160, 160) e scale=4, a janela será de 640x640 pixels (160 \* 4). +- `fps`: Define a taxa de quadros por segundo. +- `font_size`: Define o tamanho da fonte. +- `test_mode`: Ativa ou desativa o modo de teste. Quando ativado, um elemento aleatório é gerado em um local aleatório a cada quadro, o que é útil para testar a performance e a estabilidade. + +# Elementos e Controle + +- `Background` elemento padrao nao possui nenhum efeito especial +- `Sand` comportase como areia possui gravidade e afunda na agua +- `Water` simula um fluido portanto tende a se acomodar e escorrer em superficies +- `Stone` elemento estatico nao se move e impede a passagem de outros elemento +- `Vacuum` exclui elementos que entram em contato +- `Cloner` copia o primeiro elemento que o toca e gera mais desse elemento a cada quadro + +## Controle + +- Números de `1 a 6` controlam o elemento selecionado, seguindo a ordem anterior. +- Use o botão esquerdo do mouse para gerar (spawn) o novo elemento. +- Pressione a tecla `+` para aumentar o tamanho do spawn e `-` para diminuir. +- Segure `CTRL` + `+` ou `CTRL` + `-` para aumentar ou diminuir o tamanho do spawn em incrementos de 10. + +### exemplo + +![exemplo](./gifs/example1.gif) + +# Contribuições + +Infelizmente, este projeto está descontinuado. Caso você queira fazer alguma adição ou melhoria, sinta-se à vontade para fazer um fork e modificar o código para uso próprio. + +## Licença + +Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](./LICENCE) para mais detalhes. + +## Contato + +Lucas - [lucas.prokalil2020@outlook.com](mailto:lucas.prokalil2020@outlook.com) + +Repositório no GitHub: [sand-box-python](https://github.com/LucasKalil-Programador/sand-box-python)