Jump to content

Egentillverkade spel!


dibel
 Share

Recommended Posts

Jag försökte söka efter en sådan här tråd, men fann ingen så jag gör en. (Finns det en tråd för detta ämne så sammanfoga gärna!)

Här kan vi diskutera kod, grafik, design överlag m.m. för de spel vi vill tillverka eller som vi tillverkar.

---

Just nu går jag Digitalt Skapande 1 på Komvux och hade tänkt mig göra ett Pacman-liknande spel som projekt. Grafiken är gjord med Inkscape och Gimp och spelmotorn jag använder är Godot (version 3.1, akutell "stable").

Det är mest på "Hello World"-nivå än så länge, men kika gärna här: http://pacman.erikp.se (OBS: STÄNG AV LJUDET!!! Man behöver även klicka i spelfönstret för att fånga tangentbordet. WASD-styrning)

"Arbetet" kommer fortgå löpande tills kursen är klar i slutet av Maj.

-

Har ni några spel att visa upp? Några speciella tekniker? Själv använder jag bara Fri Mjukvara. Dels är det gratis, men licensmässigt är det också simplare då det är ganska tydligt vad man får/måste göra med de olika licenserna. Exempelvis spelmotorn Godot använder MIT-licens vilket enligt mig är den bästa då man får göra i princip vad man vill (främst förändra källkoden utan förhinder, men också att stänga källkoden vid behov).

2019 är året för Free Software Gaming. ;)

Redigerad av dibel
  • Gilla 2
Länk till kommentar
Dela på andra sidor

Najs! :D Nej, jag har inget eget men så fort ett spel har en map editor som inte kräver decennier av 3D-kunskaper är jag där och leker.

Brorsan gjorde några spel till ett SVT-program en gång i tiden, där man varje vecka skulle skicka in ett egenkodat spel. De lade tyvärr ned tävlingen redan efter andra avsnittet av nån anledning, så mer än så blev det inte. Han gjorde ett snake-liknande spel som var rätt smart kodat, det gick nämligen ut på att banorna blev autogenererade hinder på så sätt att man inte behövde göra nya banor manuellt med mycket tidsinvestering :)

Länk till kommentar
Dela på andra sidor

3 timmar sedan, Henkibojj skrev:

Najs! :D Nej, jag har inget eget men så fort ett spel har en map editor som inte kräver decennier av 3D-kunskaper är jag där och leker.

Brorsan gjorde några spel till ett SVT-program en gång i tiden, där man varje vecka skulle skicka in ett egenkodat spel. De lade tyvärr ned tävlingen redan efter andra avsnittet av nån anledning, så mer än så blev det inte. Han gjorde ett snake-liknande spel som var rätt smart kodat, det gick nämligen ut på att banorna blev autogenererade hinder på så sätt att man inte behövde göra nya banor manuellt med mycket tidsinvestering :)

Intressant! Metoden kallas väl för Procedural Generation? Får man till det riktigt bra så är det ju guld värt. Tänk ett RPG med Den Förtrollade Skogen som autogenereras och är lätt att gå vilse i. Har själv tänkt i de banorna!

Tilägg: Gillar du map editor kan du kika på denna: https://fornclake.com/projects/godot-level-editor-test

Han har även en tutorial (inklusive assets) för hur man kodar en simpel Zelda-prototyp. Följde den halvvägs för några månader sedan. Såg nu att han även har ett 3D-projekt igång. Finns här (ingen tutorial): https://www.youtube.com/watch?v=Lq5Dmo6t46s

---

Har fått till "Auto Tiles" igår så att rita upp banor i Godot är väldigt enkelt. Och en vägg-tile är ju bara kvadrater som ligger i lager och är olika stora/synliga. Mycket enkelt att få till i Inkscape.

Märkte förvisso att om man exporterar med nedskalning i Inkscape så blir det någon slags transparens här och där (i utkanten av bilden). Löste genom att exportera hela ritytan (2048x2048) och skala ned i Gimp istället.

Så tilesen är färdiga (man blir aldrig färdig...), men Pacman-gubben är gjord med den gamla metoden och behöver göras om. Hade ändå tänkt göra om Pacman då det nu är 21 frames för Öppen Mun > Stängd Mun vilket är alldeles för många. 8 frames bör räcka.

Redigerad av dibel
Länk till kommentar
Dela på andra sidor

en timme sedan, dibel skrev:

Har fått till "Auto Tiles" igår så att rita upp banor i Godot är väldigt enkelt. Och en vägg-tile är ju bara kvadrater som ligger i lager och är olika stora/synliga. Mycket enkelt att få till i Inkscape.

Märkte förvisso att om man exporterar med nedskalning i Inkscape så blir det någon slags transparens här och där (i utkanten av bilden). Löste genom att exportera hela ritytan (2048x2048) och skala ned i Gimp istället.

Så tilesen är färdiga (man blir aldrig färdig...), men Pacman-gubben är gjord med den gamla metoden och behöver göras om. Hade ändå tänkt göra om Pacman då det nu är 21 frames för Öppen Mun > Stängd Mun vilket är alldeles för många. 8 frames bör räcka.

Jag tror inte han gjorde det helt automatiskt, det handlade nog mer om att han möjliggjorde snabba bandesigner genom ett rutnät och på eller av för huruvida det skulle finnas block där eller inte, jämfört med de hoppaskjuta-banor han gjort till andra spel som krävde tiotals timmar för varje rum.

Kul att se att du lägger energin på något nyttigt och självutvecklande! Det är verkligen en fröjd att se dig ha hittat något du vill fördjupa dig i :)

  • Tack! 1
Länk till kommentar
Dela på andra sidor

  • 5 years later...

Om någon är intresserad av den keffa koden till spelet jag nämnde 2019 så finns den här tillsammans med arbetsfiler för pacman-gubben, spökena och tiles: https://github.com/erikp121/pacman-godot/tree/master

Inkscape för .svg-filerna, Godot 3.x (3.1 användes, nuvarande 3-version är 3.5.3 och bör vara kompatibel med koden, har inte provat själv) för Godot-projektet.

Det var som sagt ett komvux-projekt och just nu håller jag på konvertera ett annat projekt gjort i samma klass av en annan (HTML/CSS/Javascript) där jag gjorde riktigt dålig javascriptkod (som jag omstrukturerat) och använder som bas för att göra i Godot 4.3 beta 3.

Skillnaden är att jag nu bara har en Main-scene (vanlig Node) och ett main-script tillsammans med en global Autoload (main.gd + global.gd) och skapar alla Nodes i kod (GDScript).

Ganska kul att hålla på med och man lär sig mer och mer. Nu inatt när jag gick och lade mig fick jag en uppenbarelse med ett problem jag hade som jag kom på att lösa genom att använda set_anchors_and_offsets_preset() istället för set_anchors_preset() för att placera Control/UI-noder.

För större projekt kan jag tänka mig att Scenes är bättre (exporterade som klass-objekt) för struktur, men gillar verkligen enkelheten av en single source-fil.

Jag kollade en guide för hur man kan animera TextureRect istället för att använda AnimatedSprite2D som jag hade tänkt från början så det är väl nästa projekt nu när grundkoden och grundfunktionerna är "klara".

Här är main.gd för den som är intresserad (inte alls långt och komplicerat enligt mig):

Dölj innehåll
extends Node

@onready var current_animal := {
	"text": "default",
	"food": "default",
	"texture": preload("res://icon.svg"),
}

func _ready() -> void:
	create_nodes()
	create_new_animal()
	return

func _process(delta: float) -> void:
	Global.CENTER_OF_SCREEN = get_child(Global.WORLD_NODE).get_viewport_rect().size / 2
	get_animal_node().global_position = Global.CENTER_OF_SCREEN
	if Input.is_action_just_pressed("test_orphans"):
		print("===ORPHANS===")
		print_orphan_nodes()
	if Input.is_action_just_pressed("window_stuff"):
		print("window stuff")
	return

func tmp_v_box_container() -> Control:
	var v_box := VBoxContainer.new()
	v_box.set_layout_direction(Control.LAYOUT_DIRECTION_INHERITED)
	v_box.alignment = BoxContainer.ALIGNMENT_BEGIN
	v_box.add_child(tmp_quit_button())
	for b in tmp_resolution_buttons():
		v_box.add_child(b)
	v_box.add_child(tmp_fullscreen_button())
	var m_c := MarginContainer.new()
	m_c.add_child(v_box)
	m_c.set_layout_direction(Control.LAYOUT_DIRECTION_INHERITED)
	m_c.set_mouse_filter(Control.MOUSE_FILTER_IGNORE)
	m_c.set_anchors_and_offsets_preset(Control.PRESET_TOP_RIGHT)
	var n := Control.new()
	n.set_layout_direction(Control.LAYOUT_DIRECTION_LTR)
	n.set_mouse_filter(Control.MOUSE_FILTER_IGNORE)
	n.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
	n.add_child(m_c)
	return n

func tmp_food_container() -> Control:
	var h_box := HBoxContainer.new()
	h_box.set_layout_direction(Control.LAYOUT_DIRECTION_INHERITED)
	h_box.alignment = BoxContainer.ALIGNMENT_BEGIN
	for a in Global.animals:
		h_box.add_child(food_button_node(a.food))
	var m_c := MarginContainer.new()
	m_c.add_child(h_box)
	m_c.set_layout_direction(Control.LAYOUT_DIRECTION_INHERITED)
	m_c.set_mouse_filter(Control.MOUSE_FILTER_IGNORE)
	m_c.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM)
	var n := Control.new()
	n.set_layout_direction(Control.LAYOUT_DIRECTION_LTR)
	n.set_mouse_filter(Control.MOUSE_FILTER_IGNORE)
	n.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
	n.add_child(m_c)
	return n

func tmp_quit_button() -> Button:
	var b := Button.new()
	b.text = "Quit"
	b.pressed.connect(quit_button_pressed)
	return b

func tmp_resolution_buttons() -> Array:
	var b_1k := Button.new()
	b_1k.text = "720p"
	b_1k.pressed.connect(resolution_buttons.bind("720p"))
	var b_2k := Button.new()
	b_2k.text = "1080p"
	b_2k.pressed.connect(resolution_buttons.bind("1080p"))
	var b_4k := Button.new()
	b_4k.text = "4k"
	b_4k.pressed.connect(resolution_buttons.bind("4k"))
	return [b_1k, b_2k, b_4k]

func tmp_fullscreen_button() -> Button:
	var b := Button.new()
	b.text = "Fullscreen"
	b.pressed.connect(toggle_fullscreen)
	return b

func toggle_fullscreen() -> void:
	if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
		DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
	else:
		DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
	return

func tmp_setup_ui() -> void:
	get_v_box_node().add_child(tmp_quit_button())
	for b in tmp_resolution_buttons():
		get_v_box_node().add_child(b)
	get_v_box_node().add_child(tmp_fullscreen_button())
	return

func resolution_buttons(b: String) -> void:
	match b:
		"720p":
			get_viewport().set_size(Global.RESOLUTION_720)
			get_viewport().set_content_scale_size(Global.RESOLUTION_720)
		"1080p":
			get_viewport().set_size(Global.RESOLUTION_1080)
			get_viewport().set_content_scale_size(Global.RESOLUTION_1080)
		"4k":
			get_viewport().set_size(Global.RESOLUTION_4K)
			get_viewport().set_content_scale_size(Global.RESOLUTION_4K)
		_:
			print(b)
	return

func quit_button_pressed() -> void:
	get_tree().quit()
	return

func create_nodes() -> void:
	var world := Node2D.new()
	add_child(world)
	world.add_child(canvas_layer_node())
	return

func canvas_layer_node() -> CanvasLayer:
	var cv := CanvasLayer.new()
	cv.add_child(background_node())
	cv.add_child(animal_node())
	cv.add_child(tmp_v_box_container())
	cv.add_child(tmp_food_container())
	return cv

func background_node() -> Control:
	var n = Control.new()
	n.set_layout_direction(Control.LAYOUT_DIRECTION_LTR)
	n.set_mouse_filter(Control.MOUSE_FILTER_IGNORE)
	n.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
	var bg = TextureRect.new()
	bg.texture = load("res://assets/bg.png")
	bg.set_mouse_filter(TextureRect.MOUSE_FILTER_IGNORE)
	bg.set_anchors_and_offsets_preset(TextureRect.PRESET_FULL_RECT)
	bg.stretch_mode = TextureRect.STRETCH_SCALE
	bg.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
	n.add_child(bg)
	return n

func animal_node() -> Node2D:
	var n := Node2D.new()
	var TEXTURE_SCALE := 1
	var TEXTURE_SIZE := 512
	var a := Area2D.new()
	a.input_event.connect(mouse_clicked_on_animal)
	n.add_child(a)
	var s := Sprite2D.new()
	s.texture = load("res://icon.svg")
	s.scale = Vector2(TEXTURE_SCALE, TEXTURE_SCALE)
	a.add_child(s)
	var cs := CollisionShape2D.new()
	cs.set_shape(RectangleShape2D.new())
	cs.get_shape().set_size(Vector2(TEXTURE_SIZE, TEXTURE_SIZE))
	a.add_child(cs)
	return n

func food_button_node(food: String) -> Button:
	var b := Button.new()
	b.text = food.capitalize()
	b.pressed.connect(feed_animal.bind(food))
	return b

func feed_animal(food) -> void:
	if food == current_animal.food:
		print("GOOD")
		create_new_animal()
	else:
		print("BAD")
	return

func get_animal_node() -> Node2D:
	return get_child(Global.WORLD_NODE).get_child(Global.CANVAS_LAYER_NODE).get_child(Global.ANIMAL_NODE)

func get_v_box_node() -> VBoxContainer:
	return get_child(Global.WORLD_NODE).get_child(Global.CANVAS_LAYER_NODE).get_child(Global.TMP_MENU_NODE).get_child(Global.V_BOX_NODE)

func mouse_clicked_on_animal(viewport: Node, event: InputEvent, shape_idx: int) -> void:
	if Input.is_action_just_pressed("mouse_click"):
		create_new_animal()
	return

func get_random_animal_from_array() -> Dictionary:
	return Global.animals[randi() % Global.animals.size()]

func get_random_animal() -> Dictionary:
	var animal = get_random_animal_from_array()
	while animal.text == current_animal.text:
		animal = get_random_animal_from_array()
	return animal

func create_new_animal() -> void:
	var animal := get_random_animal()
	current_animal = animal
	change_animal_sprite(animal)
	return

func change_animal_sprite(animal: Dictionary) -> void:
	get_animal_sprite_node().texture = animal.texture
	return

func get_animal_sprite_node() -> Sprite2D:
	return get_animal_node().get_child(Global.ANIMAL_AREA_2D_NODE).get_child(Global.ANIMAL_SPRITE_NODE)

 

Som sagt ska jag byta ut Node2D (/Area2D/Sprite2D + CollisionShape2D) mot TextureRect och animera den istället...

GDScript är väldigt simpelt och kul att hålla på med dessutom när man grafiskt får svar så snabbt genom att köra programmet. Enkelt att exportera till de vanliga platformarna också (Windows, Linux, Mac, Android, iOS, Web) så det är en fördel gentemot C# som också stöds som scriptspråk.

Huruvida koden är bra eller inte kan jag inte svara på, men min mentalitet är "funkar det så funkar det", hehe.

Länk till kommentar
Dela på andra sidor

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gäst
Skriv inlägg...

×   Innehåll kopierat inklusive formatering.   Ta bort formatering

  Only 75 emoji are allowed.

×   Din länk har expanderats till ett media-block.   Visa länk istället

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...