{"id":19769416,"url":"https://github.com/goplus/spx","last_synced_at":"2026-01-17T01:56:48.936Z","repository":{"id":36974291,"uuid":"389782469","full_name":"goplus/spx","owner":"goplus","description":"spx - A Scratch Compatible Go/Go+ 2D Game Engine for STEM education","archived":false,"fork":false,"pushed_at":"2025-03-14T03:48:16.000Z","size":7375,"stargazers_count":109,"open_issues_count":65,"forks_count":31,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-15T05:19:33.835Z","etag":null,"topics":["builder","game-engine-2d","go","golang","gop","goplus","learning-gop","scratch-like","stem","stem-education"],"latest_commit_sha":null,"homepage":"https://builder.goplus.org","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/goplus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-26T22:18:57.000Z","updated_at":"2025-03-05T05:33:16.000Z","dependencies_parsed_at":"2024-12-17T02:25:21.020Z","dependency_job_id":"4c07cd91-e456-43d2-82e3-6d3d910439a5","html_url":"https://github.com/goplus/spx","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goplus%2Fspx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goplus%2Fspx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goplus%2Fspx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/goplus%2Fspx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/goplus","download_url":"https://codeload.github.com/goplus/spx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157281,"owners_count":20893220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["builder","game-engine-2d","go","golang","gop","goplus","learning-gop","scratch-like","stem","stem-education"],"created_at":"2024-11-12T04:43:04.440Z","updated_at":"2025-04-04T10:07:35.253Z","avatar_url":"https://github.com/goplus.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"spx - A Scratch Compatible 2D Game Engine\n========\n\n[![Build Status](https://github.com/goplus/spx/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/spx/actions/workflows/go.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/goplus/spx)](https://goreportcard.com/report/github.com/goplus/spx)\n[![GitHub release](https://img.shields.io/github/v/tag/goplus/spx.svg?label=release)](https://github.com/goplus/spx/releases)\n[![Language](https://img.shields.io/badge/language-Go+-blue.svg)](https://github.com/goplus/gop)\n[![Scratch diff](https://img.shields.io/badge/compare-Scratch-green.svg)](https://github.com/xushiwei/goplus-spx-vs-scratch/blob/main/scratch-vs-spx-v1.0.0-beta3.pdf)\n\n## How to build\n\nHow to run games powered by Go+ spx engine?\n\n* Download Go+ and build it. See https://github.com/goplus/gop#how-to-build.\n* Download spx and build it.\n  * git clone `https://github.com/goplus/spx.git`\n  * cd spx\n  * go install -v ./...\n* Build a game and run.\n  * cd `game-root-dir`\n  * gop run .\n\n\n## Games powered by spx\n\n* [AircraftWar](https://github.com/goplus/AircraftWar)\n* [FlappyCalf](https://github.com/goplus/FlappyCalf)\n* [MazePlay](https://github.com/goplus/MazePlay)\n* [BetaGo](https://github.com/xushiwei/BetaGo)\n* [Gobang](https://github.com/xushiwei/Gobang)\n* [Dinosaur](https://github.com/xushiwei/Dinosaur)\n\n\n## Tutorials\n\n### tutorial/01-Weather\n\n![Screen Shot1](tutorial/01-Weather/1.jpg) ![Screen Shot2](tutorial/01-Weather/2.jpg)\n\nThrough this example you can learn how to listen events and do somethings.\n\nHere are some codes in [Kai.spx](tutorial/01-Weather/Kai.spx):\n\n```coffee\nonStart =\u003e {\n\tsay \"Where do you come from?\", 2\n\tbroadcast \"1\"\n}\n\nonMsg \"2\", =\u003e {\n\tsay \"What's the climate like in your country?\", 3\n\tbroadcast \"3\"\n}\n\nonMsg \"4\", =\u003e {\n\tsay \"Which seasons do you like best?\", 3\n\tbroadcast \"5\"\n}\n```\n\nWe call `onStart` and `onMsg` to listen events. `onStart` is called when the program is started. And `onMsg` is called when someone calls `broadcast` to broadcast a message.\n\nWhen the program starts, Kai says `Where do you come from?`, and then broadcasts the message `1`. Who will recieve this message? Let's see codes in [Jaime.spx](tutorial/01-Weather/Jaime.spx):\n\n```coffee\nonMsg \"1\", =\u003e {\n\tsay \"I come from England.\", 2\n\tbroadcast \"2\"\n}\n\nonMsg \"3\", =\u003e {\n\tsay \"It's mild, but it's not always pleasant.\", 4\n\t# ...\n\tbroadcast \"4\"\n}\n```\n\nYes, Jaime recieves the message `1` and says `I come from England.`. Then he broadcasts the message `2`. Kai recieves it and says `What's the climate like in your country?`.\n\nThe following procedures are very similar. In this way you can implement dialogues between multiple actors.\n\n### tutorial/02-Dragon\n\n![Screen Shot1](tutorial/02-Dragon/1.jpg)\n\nThrough this example you can learn how to define variables and show them on the stage.\n\nHere are all the codes of [Dragon](tutorial/02-Dragon/Dragon.spx):\n\n```coffee\nvar (\n\tscore int\n)\n\nonStart =\u003e {\n\tscore = 0\n\tfor {\n\t\tturn rand(-30, 30)\n\t\tstep 5\n\t\tif touching(\"Shark\") {\n\t\t\tscore++\n\t\t\tplay chomp, true\n\t\t\tstep -100\n\t\t}\n\t}\n}\n```\n\nWe define a variable named `score` for `Dragon`. After the program starts, it moves randomly. And every time it touches `Shark`, it gains one score.\n\nHow to show the `score` on the stage? You don't need write code, just add a `stageMonitor` object into [assets/index.json](tutorial/02-Dragon/assets/index.json):\n\n```json\n{\n  \"zorder\": [\n    {\n      \"type\": \"monitor\",\n      \"name\": \"dragon\",\n      \"size\": 1,\n      \"target\": \"Dragon\",\n      \"val\": \"getVar:score\",\n      \"color\": 15629590,\n      \"label\": \"score\",\n      \"mode\": 1,\n      \"x\": 5,\n      \"y\": 5,\n      \"visible\": true\n    }\n  ]\n}\n```\n\n### tutorial/03-Clone\n\n![Screen Shot1](tutorial/03-Clone/1.png)\n\nThrough this example you can learn:\n* Clone sprites and destory them.\n* Distinguish between sprite variables and shared variables that can access by all sprites.\n\nHere are some codes in [Calf.spx](tutorial/03-Clone/Calf.spx):\n\n```coffee\nvar (\n\tid int\n)\n\nonClick =\u003e {\n\tclone\n}\n\nonCloned =\u003e {\n\tgid++\n\t...\n}\n```\n\nWhen we click the sprite `Calf`, it receives an `onClick` event. Then it calls `clone` to clone itself. And after cloning, the new `Calf` sprite will receive an `onCloned` event.\n\nIn `onCloned` event, the new `Calf` sprite uses a variable named `gid`. It doesn't define in [Calf.spx](tutorial/03-Clone/Calf.spx), but in [main.spx](tutorial/03-Clone/main.spx).\n\n\nHere are all the codes of [main.spx](tutorial/03-Clone/main.spx):\n\n```coffee\nvar (\n\tArrow Arrow\n\tCalf  Calf\n\tgid   int\n)\n\nrun \"res\", {Title: \"Clone and Destory (by Go+)\"}\n```\n\nAll these three variables in [main.spx](tutorial/03-Clone/main.spx) are shared by all sprites. `Arrow` and `Calf` are sprites that exist in this project. `gid` means `global id`. It is used to allocate id for all cloned `Calf` sprites.\n\nLet's back to [Calf.spx](tutorial/03-Clone/Calf.spx) to see the full codes of `onCloned`:\n\n```coffee\nonCloned =\u003e {\n\tgid++\n\tid = gid\n\tstep 50\n\tsay id, 0.5\n}\n```\n\nIt increases `gid` value and assigns it to sprite `id`. This makes all these `Calf` sprites have different `id`. Then the cloned `Calf` moves forward 50 steps and says `id` of itself.\n\nWhy these `Calf` sprites need different `id`? Because we want destory one of them by its `id`.\n\nHere are all the codes in [Arrow.spx](tutorial/03-Clone/Arrow.spx):\n\n```coffee\nonClick =\u003e {\n\tbroadcast \"undo\", true\n\tgid--\n}\n```\n\nWhen we click `Arrow`, it broadcasts an \"undo\" message (NOTE: We pass the second parameter `true` to broadcast to indicate we wait all sprites to finish processing this message).\n\nAll `Calf` sprites receive this message, but only the last cloned sprite finds its `id` is equal to `gid` then destroys itself. Here are the related codes in [Calf.spx](tutorial/03-Clone/Calf.spx):\n\n```coffee\nonMsg \"undo\", =\u003e {\n\tif id == gid {\n\t\tdestroy\n\t}\n}\n```\n\n### tutorial/04-Bullet\n\n![Screen Shot1](tutorial/04-Bullet/1.jpg)\n\nThrough this example you can learn:\n* How to keep a sprite following mouse position.\n* How to fire bullets.\n\nIt's simple to keep a sprite following mouse position. Here are some related codes in [MyAircraft.spx](tutorial/04-Bullet/MyAircraft.spx):\n\n\n```coffee\nonStart =\u003e {\n\tfor {\n\t\t# ...\n\t\tsetXYpos mouseX, mouseY\n\t}\n}\n```\n\nYes, we just need to call `setXYpos mouseX, mouseY` to follow mouse position.\n\nBut how to fire bullets? Let's see all codes of [MyAircraft.spx](tutorial/04-Bullet/MyAircraft.spx):\n\n```coffee\nonStart =\u003e {\n\tfor {\n\t\twait 0.1\n\t\tBullet.clone\n\t\tsetXYpos mouseX, mouseY\n\t}\n}\n```\n\nIn this example, `MyAircraft` fires bullets every 0.1 seconds. It just calls `Bullet.clone` to create a new bullet. All the rest things are the responsibility of `Bullet`.\n\nHere are all the codes in [Bullet.spx](tutorial/04-Bullet/Bullet.spx):\n\n```coffee\nonCloned =\u003e {\n\tsetXYpos MyAircraft.xpos, MyAircraft.ypos+5\n\tshow\n\tfor {\n\t\twait 0.04\n\t\tchangeYpos 10\n\t\tif touching(Edge) {\n\t\t\tdestroy\n\t\t}\n\t}\n}\n```\n\nWhen a `Bullet` is cloned, it calls `setXYpos MyAircraft.xpos, MyAircraft.ypos+5` to follow `MyAircraft`'s position and shows itself (the default state of a `Bullet` is hidden). Then the `Bullet` moves forward every 0.04 seconds and this is why we see the `Bullet` is flying.\n\nAt last, when the `Bullet` touches screen `Edge` or any enemy (in this example we don't have enemies), it destroys itself.\n\nThese are all things about firing bullets.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoplus%2Fspx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgoplus%2Fspx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgoplus%2Fspx/lists"}