From 6f6230b8993b647f8fdf6447683f1c772d9a71c4 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Thu, 8 Jul 2021 21:55:16 -0400 Subject: [PATCH] Create Pong with GDNative C++ demo --- .gitmodules | 3 + cpp/pong/README.md | 44 ++++++++ cpp/pong/SConstruct | 111 +++++++++++++++++++ cpp/pong/project/ball.png | Bin 0 -> 115 bytes cpp/pong/project/ball.png.import | 34 ++++++ cpp/pong/project/gdnative/linux/.gitignore | 2 + cpp/pong/project/gdnative/osx/.gitignore | 2 + cpp/pong/project/gdnative/pong.gdnlib | 18 +++ cpp/pong/project/gdnative/windows/.gitignore | 2 + cpp/pong/project/icon.png | Bin 0 -> 549 bytes cpp/pong/project/icon.png.import | 34 ++++++ cpp/pong/project/logic/ball.gdns | 9 ++ cpp/pong/project/logic/ceiling_floor.gdns | 9 ++ cpp/pong/project/logic/paddle.gdns | 9 ++ cpp/pong/project/logic/wall.gdns | 9 ++ cpp/pong/project/paddle.png | Bin 0 -> 84 bytes cpp/pong/project/paddle.png.import | 34 ++++++ cpp/pong/project/pong.tscn | 107 ++++++++++++++++++ cpp/pong/project/project.godot | 72 ++++++++++++ cpp/pong/project/separator.png | Bin 0 -> 108 bytes cpp/pong/project/separator.png.import | 34 ++++++ cpp/pong/screenshots/pong.png | Bin 0 -> 481 bytes cpp/pong/src/ball.cpp | 24 ++++ cpp/pong/src/ball.hpp | 25 +++++ cpp/pong/src/ceiling_floor.cpp | 12 ++ cpp/pong/src/ceiling_floor.hpp | 21 ++++ cpp/pong/src/entry.cpp | 23 ++++ cpp/pong/src/paddle.cpp | 40 +++++++ cpp/pong/src/paddle.hpp | 29 +++++ cpp/pong/src/wall.cpp | 12 ++ cpp/pong/src/wall.hpp | 19 ++++ 31 files changed, 738 insertions(+) create mode 100644 cpp/pong/README.md create mode 100644 cpp/pong/SConstruct create mode 100644 cpp/pong/project/ball.png create mode 100644 cpp/pong/project/ball.png.import create mode 100644 cpp/pong/project/gdnative/linux/.gitignore create mode 100644 cpp/pong/project/gdnative/osx/.gitignore create mode 100644 cpp/pong/project/gdnative/pong.gdnlib create mode 100644 cpp/pong/project/gdnative/windows/.gitignore create mode 100644 cpp/pong/project/icon.png create mode 100644 cpp/pong/project/icon.png.import create mode 100644 cpp/pong/project/logic/ball.gdns create mode 100644 cpp/pong/project/logic/ceiling_floor.gdns create mode 100644 cpp/pong/project/logic/paddle.gdns create mode 100644 cpp/pong/project/logic/wall.gdns create mode 100644 cpp/pong/project/paddle.png create mode 100644 cpp/pong/project/paddle.png.import create mode 100644 cpp/pong/project/pong.tscn create mode 100644 cpp/pong/project/project.godot create mode 100644 cpp/pong/project/separator.png create mode 100644 cpp/pong/project/separator.png.import create mode 100644 cpp/pong/screenshots/pong.png create mode 100644 cpp/pong/src/ball.cpp create mode 100644 cpp/pong/src/ball.hpp create mode 100644 cpp/pong/src/ceiling_floor.cpp create mode 100644 cpp/pong/src/ceiling_floor.hpp create mode 100644 cpp/pong/src/entry.cpp create mode 100644 cpp/pong/src/paddle.cpp create mode 100644 cpp/pong/src/paddle.hpp create mode 100644 cpp/pong/src/wall.cpp create mode 100644 cpp/pong/src/wall.hpp diff --git a/.gitmodules b/.gitmodules index 65294b3..d8a7489 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "cpp/simple/godot-cpp"] path = cpp/simple/godot-cpp url = https://github.com/godotengine/godot-cpp.git +[submodule "cpp/pong/godot-cpp"] + path = cpp/pong/godot-cpp + url = https://github.com/godotengine/godot-cpp.git diff --git a/cpp/pong/README.md b/cpp/pong/README.md new file mode 100644 index 0000000..c409c10 --- /dev/null +++ b/cpp/pong/README.md @@ -0,0 +1,44 @@ +# Pong with GDNative C++ + +A simple Pong game, made with GDNative C++. This demo shows best practices +for game development in Godot, including +[signals](https://docs.godotengine.org/en/latest/getting_started/step_by_step/signals.html). + +Language: C++ + +Renderer: GLES 2 + +Note: There is a GDScript version available [here](https://github.com/godotengine/godot-demo-projects/tree/master/2d/pong). + +Note: There is a C# version available [here](https://github.com/godotengine/godot-demo-projects/tree/master/mono/pong). + +Note: There is a VisualScript version available [here](https://github.com/godotengine/godot-demo-projects/tree/master/visual_script/pong). + +## Compiling + +You can use SCons to compile the library: + +``` +scons platform=PLATFORM +``` + +Where PLATFORM is: `windows`, `linux`, or `osx`. + +This creates the file `libsimple` in the respective +subfolders in the `project/gdnative` directory. + +Dependencies: + * You need [godot-cpp](https://github.com/godotengine/godot-cpp), + this is now a Git submodule of this repo. + * `clang`, `gcc`, or any decent C compiler that's C++14 compatible. + +## How does it work? + +The walls, paddle, and ball are all +[`Area2D`](https://docs.godotengine.org/en/latest/classes/class_area2d.html) +nodes. When the ball touches the walls or the paddles, +they emit signals and modify the ball. + +## Screenshots + +![Screenshot](screenshots/pong.png) diff --git a/cpp/pong/SConstruct b/cpp/pong/SConstruct new file mode 100644 index 0000000..71ac6e7 --- /dev/null +++ b/cpp/pong/SConstruct @@ -0,0 +1,111 @@ +#!python +import os + +opts = Variables([], ARGUMENTS) + +# Gets the standard flags CC, CCX, etc. +env = DefaultEnvironment() + +godot_headers_path = "godot-cpp/godot-headers" +godot_bindings_path = "godot-cpp" + +# Define our options. Use future-proofed names for platforms. +platform_array = ["", "windows", "linuxbsd", "macos", "x11", "linux", "osx"] +opts.Add(EnumVariable("target", "Compilation target", "debug", ["d", "debug", "r", "release"])) +opts.Add(EnumVariable("platform", "Compilation platform", "", platform_array)) +opts.Add(EnumVariable("p", "Alias for 'platform'", "", platform_array)) +opts.Add(BoolVariable("use_llvm", "Use the LLVM / Clang compiler", "no")) +opts.Add(PathVariable("target_path", "The path where the lib is installed.", "project/gdnative/")) +opts.Add(PathVariable("target_name", "The library name.", "libpong", PathVariable.PathAccept)) + +# Updates the environment with the option variables. +opts.Update(env) + +# Process platform arguments. Here we use the same names as GDNative. +if env["p"] != "": + env["platform"] = env["p"] + +if env["platform"] == "macos": + env["platform"] = "osx" +elif env["platform"] in ("x11", "linuxbsd"): + env["platform"] = "linux" +elif env["platform"] == "bsd": + env["platform"] = "freebsd" + +if env["platform"] == "": + print("No valid target platform selected.") + quit() + +platform = env["platform"] + +# Process other arguments. +if env["platform"] == "osx" and not env["use_llvm"]: + env["use_llvm"] = "yes" + +if env["use_llvm"] == "yes": + env["CC"] = "clang" + env["CXX"] = "clang++" + +# put stuff that is the same for all first, saves duplication +if env["platform"] == "osx": + if env["target"] in ("debug", "d"): + env.Append(CCFLAGS=["-g", "-O2", "-arch", "x86_64", "-std=c++14"]) + env.Append(LINKFLAGS=["-arch", "x86_64"]) + else: + env.Append(CCFLAGS=["-g", "-O3", "-arch", "x86_64", "-std=c++14"]) + env.Append(LINKFLAGS=["-arch", "x86_64"]) +elif env["platform"] == "linux": + if env["target"] in ("debug", "d"): + env.Append(CCFLAGS=["-fPIC", "-g3", "-Og"]) + else: + env.Append(CCFLAGS=["-fPIC", "-g", "-O3"]) +elif env["platform"] == "windows": + # This makes sure to keep the session environment variables + # on Windows, so that you can run scons in a VS 2017 prompt + # and it will find all the required tools. + env = Environment(ENV=os.environ) + opts.Update(env) + + env.Append(CCFLAGS=["-DWIN32", "-D_WIN32", "-D_WINDOWS", "-W3", "-GR", "-D_CRT_SECURE_NO_WARNINGS"]) + if env["target"] in ("debug", "d"): + env.Append(CCFLAGS=["-EHsc", "-D_DEBUG", "-MDd"]) + else: + env.Append(CCFLAGS=["-O2", "-EHsc", "-DNDEBUG", "-MD"]) + +SConscript("godot-cpp/SConstruct") + + +def add_sources(sources, dir): + for f in os.listdir(dir): + if f.endswith(".cpp"): + sources.append(dir + "/" + f) + + +env.Append( + CPPPATH=[ + godot_headers_path, + godot_bindings_path + "/include", + godot_bindings_path + "/include/gen/", + godot_bindings_path + "/include/core/", + ] +) + +env.Append( + LIBS=[ + env.File( + os.path.join( + "godot-cpp/bin", "libgodot-cpp.%s.%s.64%s" % (env["platform"], env["target"], env["LIBSUFFIX"]) + ) + ) + ] +) + +env.Append(LIBPATH=[godot_bindings_path + "/bin/"]) + +sources = [] +add_sources(sources, "src") + +library = env.SharedLibrary( + target=env["target_path"] + "/" + env["platform"] + "/" + env["target_name"], source=sources +) +Default(library) diff --git a/cpp/pong/project/ball.png b/cpp/pong/project/ball.png new file mode 100644 index 0000000000000000000000000000000000000000..465d3521aa8d143ea46b540f2902a13a507b5b00 GIT binary patch literal 115 zcmeAS@N?(olHy`uVBq!ia0vp^93afZ3?z3ZhDiV^o&cW^S0Mc#2pIlx)!^6a-r8E{6*u%sB|NjL7 z0y`TU-pkAS_xBYC2R$4d>D}Go(9j+W3pyGa`}z6Kw6r4)4KWoJ{{a8Pq@*DX4D#ve zyqlXE2?-1Y1ON*Q05vrLkdWij(z}|P4+I3-#>T;+p)eE_78VwNe}CA*!UzZmEG#UO zlatJ`u{s$U+sDV!tD${J3JN?M8};w+)xEtU3=B0F z7sjZlA`K1r^Ygx)o$x+BY}k?A0002|Nkl;u~3+>pe#scPIo4mySjBx&wbx>&b`Or<5FBd2ViIi5EQ^vppI{j z-GCG%)EUe;2XFjmgbQ%eki@+dkYe{UeGma6Km-T_U;$1*KT?|Q+xx+KXwaPlTMEds z`kO||C>ydD7Jg^r!c<@%1s2Yla&V9WlA)JzCIu9k`T&&HU`+ss`%zF(e}0SxQqrC-jv*Ddk{KHR2QVZhbNz8= iKJ1nd(opb*nL+W%J%g`F{I*FGD9P?56_G*8n#~jml#xkd7nH`zu^r~p25@A&t;uc GLK6VrgCLOr literal 0 HcmV?d00001 diff --git a/cpp/pong/project/separator.png.import b/cpp/pong/project/separator.png.import new file mode 100644 index 0000000..a8527c6 --- /dev/null +++ b/cpp/pong/project/separator.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/separator.png-f981c8489b9148e2e1dc63398273da74.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://separator.png" +dest_files=[ "res://.import/separator.png-f981c8489b9148e2e1dc63398273da74.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=false +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/cpp/pong/screenshots/pong.png b/cpp/pong/screenshots/pong.png new file mode 100644 index 0000000000000000000000000000000000000000..2179d656ef617e23faba53dd7eed66e010e2f4bd GIT binary patch literal 481 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV4T3j3>0ymYqkDp8Mnc58v-N zaFx|4BZ*B?(vX0@~lj*UFCW;n7@N8-11%7^dg_|MF%=t*Y}QxiJ@vI1xi z$cjcDu-!KAD?&I*7MCqHGi+v(Ou2A`hbv`*qp-2Y1V=XCGYcHpBu%hMJovA?!R+H4 zCU1Xa(^!qbKG{%aBlN=Uty?b8Z#>*Ue1D>*(}%QAGrGc-^3rYKk7nU shpAXH#lRHk7@*letB7(O+|_#+(+o^1XQakV2F4kKr>mdKI;Vst0DH8mxBvhE literal 0 HcmV?d00001 diff --git a/cpp/pong/src/ball.cpp b/cpp/pong/src/ball.cpp new file mode 100644 index 0000000..1a9ee36 --- /dev/null +++ b/cpp/pong/src/ball.cpp @@ -0,0 +1,24 @@ +#include "ball.hpp" + +void Ball::_ready() { + _initial_pos = get_position(); +} + +void Ball::_process(double p_delta) { + _speed += p_delta * 2; + godot::Vector2 position = get_position(); + position += _speed * p_delta * direction; + set_position(position); +} + +void Ball::reset() { + direction = godot::Vector2(-1, 0); + set_position(_initial_pos); + _speed = DEFAULT_SPEED; +} + +void Ball::_register_methods() { + godot::register_method("_ready", &Ball::_ready); + godot::register_method("_process", &Ball::_process); + godot::register_method("reset", &Ball::reset); +} diff --git a/cpp/pong/src/ball.hpp b/cpp/pong/src/ball.hpp new file mode 100644 index 0000000..c60a585 --- /dev/null +++ b/cpp/pong/src/ball.hpp @@ -0,0 +1,25 @@ +#ifndef BALL_H +#define BALL_H + +#include +#include + +class Ball : public godot::Area2D { + GODOT_CLASS(Ball, godot::Area2D) + + const int DEFAULT_SPEED = 100; + real_t _speed = DEFAULT_SPEED; + godot::Vector2 _initial_pos; + +public: + godot::Vector2 direction = godot::Vector2(-1, 0); + + void _init() {} + void _ready(); + void _process(double p_delta); + void reset(); + + static void _register_methods(); +}; + +#endif // BALL_H diff --git a/cpp/pong/src/ceiling_floor.cpp b/cpp/pong/src/ceiling_floor.cpp new file mode 100644 index 0000000..f7712ff --- /dev/null +++ b/cpp/pong/src/ceiling_floor.cpp @@ -0,0 +1,12 @@ +#include "ceiling_floor.hpp" + +void CeilingFloor::_on_area_entered(Ball *p_ball) { + if (p_ball->get_name() == "Ball") { + p_ball->direction = (p_ball->direction + godot::Vector2(0, bounce_direction)).normalized(); + } +} + +void CeilingFloor::_register_methods() { + godot::register_method("_on_area_entered", &CeilingFloor::_on_area_entered); + godot::register_property("bounce_direction", &CeilingFloor::bounce_direction, 1); +} diff --git a/cpp/pong/src/ceiling_floor.hpp b/cpp/pong/src/ceiling_floor.hpp new file mode 100644 index 0000000..0a7d538 --- /dev/null +++ b/cpp/pong/src/ceiling_floor.hpp @@ -0,0 +1,21 @@ +#ifndef CEILING_FLOOR_H +#define CEILING_FLOOR_H + +#include +#include + +#include "ball.hpp" + +class CeilingFloor : public godot::Area2D { + GODOT_CLASS(CeilingFloor, godot::Area2D) + +public: + int bounce_direction = 1; + + void _init() {} + void _on_area_entered(Ball *p_ball); + + static void _register_methods(); +}; + +#endif // CEILING_FLOOR_H diff --git a/cpp/pong/src/entry.cpp b/cpp/pong/src/entry.cpp new file mode 100644 index 0000000..398d5d0 --- /dev/null +++ b/cpp/pong/src/entry.cpp @@ -0,0 +1,23 @@ +#include + +#include "ball.hpp" +#include "ceiling_floor.hpp" +#include "paddle.hpp" +#include "wall.hpp" + +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class(); + godot::register_class(); + godot::register_class(); + godot::register_class(); +} diff --git a/cpp/pong/src/paddle.cpp b/cpp/pong/src/paddle.cpp new file mode 100644 index 0000000..fdaed7c --- /dev/null +++ b/cpp/pong/src/paddle.cpp @@ -0,0 +1,40 @@ +#include "paddle.hpp" + +#include +#include + +void Paddle::_ready() { + _screen_size_y = get_viewport_rect().size.y; + godot::String n = godot::String(get_name()).to_lower(); + _up = n + "_move_up"; + _down = n + "_move_down"; + if (n == "left") { + _ball_dir = 1; + } else { + _ball_dir = -1; + } +} + +void Paddle::_process(double delta) { + godot::Input *input = godot::Input::get_singleton(); + // Move up and down based on input. + real_t keyboard_input = input->get_action_strength(_down) - input->get_action_strength(_up); + godot::Vector2 position = get_position(); + position.y = godot::Math::clamp(position.y + keyboard_input * MOVE_SPEED * delta, 16.0, _screen_size_y - 16.0); + set_position(position); +} + +void Paddle::_on_area_entered(Ball *p_ball) { + if (p_ball->get_name() == "Ball") { + godot::Ref random = godot::RandomNumberGenerator::_new(); + random->randomize(); + // Assign new direction. + p_ball->direction = godot::Vector2(_ball_dir, random->randf() * 2 - 1).normalized(); + } +} + +void Paddle::_register_methods() { + godot::register_method("_ready", &Paddle::_ready); + godot::register_method("_process", &Paddle::_process); + godot::register_method("_on_area_entered", &Paddle::_on_area_entered); +} diff --git a/cpp/pong/src/paddle.hpp b/cpp/pong/src/paddle.hpp new file mode 100644 index 0000000..678d67a --- /dev/null +++ b/cpp/pong/src/paddle.hpp @@ -0,0 +1,29 @@ +#ifndef PADDLE_H +#define PADDLE_H + +#include +#include + +#include "ball.hpp" + +class Paddle : public godot::Area2D { + GODOT_CLASS(Paddle, godot::Area2D) + + const int MOVE_SPEED = 100; + + int _ball_dir; + godot::String _up; + godot::String _down; + + real_t _screen_size_y; + +public: + void _init() {} + void _ready(); + void _process(double p_delta); + void _on_area_entered(Ball *p_area); + + static void _register_methods(); +}; + +#endif // PADDLE_H diff --git a/cpp/pong/src/wall.cpp b/cpp/pong/src/wall.cpp new file mode 100644 index 0000000..887cd1c --- /dev/null +++ b/cpp/pong/src/wall.cpp @@ -0,0 +1,12 @@ +#include "wall.hpp" + +void Wall::_on_wall_area_entered(Ball *p_ball) { + if (p_ball->get_name() == "Ball") { + // Ball went out of game area, reset. + p_ball->reset(); + } +} + +void Wall::_register_methods() { + godot::register_method("_on_wall_area_entered", &Wall::_on_wall_area_entered); +} diff --git a/cpp/pong/src/wall.hpp b/cpp/pong/src/wall.hpp new file mode 100644 index 0000000..5604ca2 --- /dev/null +++ b/cpp/pong/src/wall.hpp @@ -0,0 +1,19 @@ +#ifndef WALL_H +#define WALL_H + +#include +#include + +#include "ball.hpp" + +class Wall : public godot::Area2D { + GODOT_CLASS(Wall, godot::Area2D) + +public: + void _init() {} + void _on_wall_area_entered(Ball *p_area); + + static void _register_methods(); +}; + +#endif // WALL_H