1. Code
  2. Mobile Development
  3. Corona

Erstellen eines Endless Runner-Spiels von Grund auf neu: Hintergrundbewegung

Scroll to top
This post is part of a series called Corona SDK: Build an Endless Runner Game From Scratch.
Corona SDK: Build an Endless Runner Game From Scratch!
Build an Endless Runner Game From Scratch: Using Sprites

German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)

Willkommen zum zweiten Tutorial in unserer Reihe zum Erstellen eines laufenden Spiels mit dem Corona SDK. In diesem Abschnitt werden wir uns damit befassen, wie Sie schnell ein grundlegendes Hintergrund-Scrolling-Level zum Laufen bringen. Öffnen Sie Ihren Texteditor und legen Sie los!


Voraussetzungen

Wenn Sie das erste Tutorial in dieser Reihe noch nicht gesehen haben, lesen Sie es schnell durch, bevor Sie mit diesem fortfahren. Wenn Sie ein wenig Erfahrung im Codieren haben, können Sie das gesamte erste Tut schnell überspringen. Wenn Sie dies nicht getan haben, sollte es sich lohnen, sich einige der Grundlagen der Programmierung mit Lua und anzuschauen das Corona SDK.


Projektaufbau

Das erste, was wir behandeln müssen, ist die Datei build.settings. Das ist eine Datei, die sich im selben Ordner wie die Datei main.lua befinden muss. Mit dieser Datei legen wir einige unserer Anwendungsspezifikationen fest. Abhängig davon, mit welcher Plattform Sie arbeiten (Android oder iOS), wird Ihre build.settings-Datei nun verwendet, um verschiedene Dinge festzulegen. Beispielsweise müssen sich Android-Entwickler mit ihrer Anwendungsmanifestdatei und ihren Berechtigungen befassen, während iOS-Entwickler sich um ihre *.plist-Datei kümmern müssen. In der Datei build.settings werden viele dieser Einstellungen auf Anwendungsebene verarbeitet. Eines der schönen Dinge am Corona SDK ist, dass wir unsere build.settings-Informationen für Android und iOS in derselben build.settings-Datei kombinieren können. Auf diese Weise müssen Sie nicht mehrere build.settings-Dateien verwalten, wenn Sie an beiden Gerätetypen arbeiten. Für unsere Zwecke verwenden wir derzeit nur die Datei build.settings, um unsere Geräteorientierung festzulegen. Erstellen Sie also eine neue Datei mit dem Namen build.settings in demselben Ordner, in dem Sie Ihre main.lua-Datei haben möchten, und fügen Sie diesen Code ein:

1
--Notice you can use comments in this section as well
2
settings = {
3
orientation = {
4
default = "landscapeRight",
5
supported = {
6
"landscapeRight", "landscapeLeft"
7
},
8
},
9
}

Das gibt uns ein landschaftsorientiertes Gerät auf iOS- und Android-Geräten. Der Code ist ziemlich einfach und wir werden mehr Einstellungen durchgehen, die Sie in zukünftigen Tutorials verwenden möchten, aber im Moment sollte dies alles sein, was wir brauchen, um vorwärts zu kommen. Wenn Sie selbst tiefer graben möchten, finden Sie hier alle build.settings-Optionen. Um sicherzustellen, dass die Datei funktioniert, führen wir einen kurzen Test im Simulator durch. Öffnen Sie Ihre Datei main.lua und geben Sie diesen Code dort ein:

1
--This line will remove the status bar at the top of screen,
2
--some apps you might want to keep it in, but not for games!
3
display.setStatusBar(display.HiddenStatusBar)
4
5
local testRect = display.newRect(0,0,50,50)
6
testRect:setFillColor(150,0,0);

Wenn sich Ihre build.settings an der richtigen Stelle befinden, sollten Sie nach all dem etwas haben, das so aussieht:

Landscape OrientationLandscape OrientationLandscape Orientation

Wenn das Gerät aufrecht steht (Hochformat) und nicht wie oben auf der Seite liegt (Querformat), ist ein Fehler aufgetreten. Hoffentlich ist dies jedoch nicht der Fall, da der obige Code alles ist, was dazu gehört. Nun, da dies nicht mehr möglich ist, können wir unser Spiel zum Laufen bringen. Das Ziel für jedes der Tutorials ist es, einen neuen Arbeitsbereich zu haben. Jeder baut auf dem letzten auf, aber nach jeder Iteration haben Sie etwas, das spielbar sein sollte.


Hintergrund hinzufügen

Das erste, was wir tun wollen, ist, unser Level in Bewegung zu bringen. Wir werden mehrere Layers in unserem Spiel haben, die mit unterschiedlichen Geschwindigkeitsstufen scrollen. Dies wird uns die Illusion geben, was wir Parallaxen-Scrollen nennen. Wenn Sie das Programm im Simulator ausführen, wählen Sie zunächst das iPhone aus (falls Sie dies noch nicht getan haben). Ändern Sie dies, indem Sie im Simulator zu Fenster > Anzeigen als > iPhone gehen. Die Assets wurden für ein Gerät mit einer Auflösung von 480 x 320 Pixel entwickelt. Es gibt natürlich Möglichkeiten, um sicherzustellen, dass es für alle Auflösungen funktioniert, aber für unsere Testzwecke bleiben Sie jetzt einfach beim iPhone-Simulator. Gehen Sie voran und fügen Sie diesen Code in Ihr Programm ein:

1
--takes away the display bar at the top of the screen
2
display.setStatusBar(display.HiddenStatusBar)
3
4
--adds an image to our game centered at x and y coordinates
5
local backbackground = display.newImage("images/background.png")
6
backbackground.x = 240
7
backbackground.y = 160
8
9
local backgroundfar = display.newImage("images/bgfar1.png")
10
backgroundfar.x = 480
11
backgroundfar.y = 160

Damit das funktioniert, verwenden Sie die von mir bereitgestellten Bilder. Damit der Code unverändert funktioniert, müssen Sie die Bilder in einem Ordner namens images ablegen. Der Bilderordner sollte sich im selben Ordner wie Ihre Datei main.lua und build.settings befinden. Sobald Sie dies tun, sollten Sie einen Bildschirm erhalten, der so aussieht:

Beachten Sie, wie sich das Hintergrundbild vor dem Hintergrund befindet? Stellen Sie sich vor, Sie stapeln mehrere Teller übereinander. Je mehr Sie stapeln, desto mehr haben Sie die erste Platte bedeckt, auf der Sie zunächst alles gestapelt haben. Das gilt auch für Bilder und Text, die Sie in Ihren Code einfügen. Jedes Mal, wenn Sie dem Bildschirm etwas hinzufügen möchten, deckt dieses hinzugefügte Element, sofern nicht anders angegeben, alles ab, worauf Sie bereits gestapelt haben. Lassen Sie uns fortfahren und ein paar weitere Dinge auf unseren Bildschirm stapeln. Fügen Sie dies Ihrem Code unter den anderen Zeilen hinzu:

1
local backgroundnear1 = display.newImage("images/bgnear2.png")
2
backgroundnear1.x = 240
3
backgroundnear1.y = 160
4
5
local backgroundnear2 = display.newImage("images/bgnear2.png")
6
backgroundnear2.x = 760
7
backgroundnear2.y = 160

Jetzt sollten wir ein weiteres Layer wie folgt darauf stapeln:

Wenn Sie sich das ansehen, werden Sie feststellen, dass, obwohl wir zwei Instanzen von bgnear2.png hinzugefügt haben, nur eine davon auf dem Bildschirm angezeigt wird. Der Grund, warum wir Dinge tun, wird etwas offensichtlicher, wenn wir anfangen, alles zu bewegen. Wechseln Sie vorerst Ihre Ansicht von iPhone zu iPhone 4 auf die gleiche Weise wie zuvor. Da wir unsere Bilder auf die Auflösung eines iPhone ohne Netzhaut (z. B. 480 x 320 Pixel) ausgelegt haben, wird bei einer höheren Auflösung wie dem iPhone 4 (z. B. 960 x 640 Pixel) alles kleiner und wir können mehr von dem sehen, was gerade passiert. Wechseln Sie zur iPhone 4-Auflösung und Sie sollten das jetzt sehen:

Wann immer wir sehen wollen, was los ist, ist es sehr hilfreich, zur größeren Ansicht zu wechseln. Beachten Sie, dass nur, weil Sie etwas nicht sehen können, dies nicht bedeutet, dass nichts passiert. Nachdem wir diese Hintergrundbilder dort haben, bewegen wir sie. Während ich die Tutorials durcharbeite, werde ich versuchen, alles zu dokumentieren, was im Code selbst geschieht. Wenn Sie sich also nie sicher sind, was passiert, lesen Sie unbedingt die Kommentare im Code! Fügen Sie nun unter dem zuvor hinzugefügten Code Folgendes hinzu:

1
--the update function will control most everything that happens in our game
2
--this will be called every frame(30 frames per second in our case, which is the Corona SDK default)
3
local function update( event )
4
--updateBackgrounds will call a function made specifically to handle the background movement
5
updateBackgrounds()
6
end
7
8
function updateBackgrounds()
9
--far background movement
10
backgroundfar.x = backgroundfar.x - (.25)
11
12
--near background movement
13
backgroundnear1.x = backgroundnear1.x - (3)
14
--if the sprite has moved off the screen move it back to the
15
--other side so it will move back on
16
if(backgroundnear1.x < -239) then
17
backgroundnear1.x = 760
18
end
19
20
backgroundnear2.x = backgroundnear2.x - (3)
21
if(backgroundnear2.x < -239) then
22
backgroundnear2.x = 760
23
end
24
end
25
26
--this is how we call the update function, make sure that this line comes after the
27
--actual function or it will not be able to find it
28
--timer.performWithDelay(how often it will run in milliseconds, function to call,
29
--how many times to call(-1 means forever))
30
timer.performWithDelay(1, update, -1)

Wenn Sie das ausführen, führen Sie es zuerst aus, während Sie sich noch in der iPhone 4-Ansicht befinden. Auf diese Weise können Sie sehen, was mit all den verschiedenen Sprites, die wir bewegen, tatsächlich passiert. Sobald Sie es in dieser Ansicht betrachtet haben, wechseln Sie zurück zur normalen iPhone-Ansicht, damit Sie sehen können, wie es wirklich aussieht. Mit all dem sollten Sie jetzt alles in Bewegung haben! Nun ein paar Dinge, die wir uns ansehen müssen, bevor wir weitermachen. Beachten Sie, dass es nur eine Instanz von backgroundfar gibt, aber zwei Instanzen von backgroundnear? Das liegt ganz bei Ihnen. Der Grund, warum es zwei gibt, ist, dass wir für den näheren Hintergrund (z. B. vorbeifliegende Bäume) möchten, dass sich dies immer wieder wiederholt. Für den fernen Hintergrund wollte ich, dass es etwas ist, das sehr langsam vorbeigeht und sich nicht wiederholt (z. B. tun Sie dies normalerweise nur für Hintergründe, die weit zurückliegen, es gibt Levels ein einzigartiges Gefühl). Das ist etwas, was Sie tun würden, wenn Ihre Bildlaufebene ein Ende haben wird. Wenn Sie wirklich einen nie endenden Scroller wollten, möchten Sie den gleichen Ansatz wie für den nahen Hintergrund wählen und zwei nebeneinander platzieren. Indem Sie zwei oder drei nebeneinander platzieren oder wie viele Sie möchten, können Sie scheinbar große Welten mit sehr geringem Vermögen erstellen. Das einzige, was wir tun müssen, ist, das Sprite zurück auf die andere Seite des Bildschirms zu verschieben.

Das nächste, was wir tun werden, ist den Boden hinzuzufügen. Das wird noch ein paar Schritte dauern, aber wir werden langsam vorgehen und sicherstellen, dass alles an der richtigen Stelle platziert wird. Wenn Sie das erste Tutorial nicht durchgeführt haben und nicht verstehen, wie man Gruppen verwendet oder was sie sind, wäre jetzt ein guter Zeitpunkt, es auszuprobieren. Hier ist der Code, den Sie einfügen müssen (platzieren Sie ihn direkt unter dem Code, in dem wir backgroundnear2 einrichten, vor der Aktualisierungsfunktion):

1
--create a new group to hold all of our blocks
2
local blocks = display.newGroup()
3
4
--setup some variables that we will use to position the ground
5
local groundMin = 420
6
local groundMax = 340
7
local groundLevel = groundMin
8
9
--this for loop will generate all of your ground pieces, we are going to
10
--make 8 in all.
11
for a = 1, 8, 1 do
12
isDone = false
13
14
--get a random number between 1 and 2, this is what we will use to decide which
15
--texture to use for our ground sprites. Doing this will give us random ground
16
--pieces so it seems like the ground goes on forever. You can have as many different
17
--textures as you want. The more you have the more random it will be, just remember to
18
--up the number in math.random(x) to however many textures you have.
19
numGen = math.random(2)
20
local newBlock
21
print (numGen)
22
if(numGen == 1 and isDone == false) then
23
newBlock = display.newImage("images/ground1.png")
24
isDone = true
25
end
26
27
if(numGen == 2 and isDone == false) then
28
newBlock = display.newImage("images/ground2.png")
29
isDone = true
30
end
31
32
--now that we have the right image for the block we are going
33
--to give it some member variables that will help us keep track
34
--of each block as well as position them where we want them.
35
newBlock.name = ("block" .. a)
36
newBlock.id = a
37
38
--because a is a variable that is being changed each run we can assign
39
--values to the block based on a. In this case we want the x position to
40
--be positioned the width of a block apart.
41
newBlock.x = (a * 79) - 79
42
newBlock.y = groundLevel
43
blocks:insert(newBlock)
44
end

Die Kommentare im Code sollen Ihnen helfen, zu verstehen, was alles bisher tut. Hoffentlich sieht das, was Sie jetzt sehen, ungefähr so aus:

Forth BackgroundForth BackgroundForth Background

Das Letzte, was wir tun müssen, ist, die Blöcke in Bewegung zu setzen. Lassen Sie uns fortfahren und den Code dafür hinzufügen. Geben Sie diesen Code nach dem Ende der Aktualisierungsfunktion ein:

1
function updateBlocks()
2
for a = 1, blocks.numChildren, 1 do
3
4
if(a > 1) then
5
newX = (blocks[a - 1]).x + 79
6
else
7
newX = (blocks[8]).x + 79
8
end
9
10
if((blocks[a]).x < -40) then
11
(blocks[a]).x, (blocks[a]).y = newX, groundLevel
12
else
13
(blocks[a]):translate(-5, 0)
14
end
15
16
end
17
end

Bevor sich etwas tatsächlich bewegt, müssen wir sicherstellen, dass wir die Funktion tatsächlich über die Aktualisierungsfunktion aufrufen. Rufen Sie innerhalb der Update-Funktion unterhalb der Zeile updateBackgrounds() die Funktion updateBlocks() auf. Damit sollten wir jetzt alles in Bewegung haben. Beachten Sie, dass wir anstelle des manuellen Verschiebens der Blöcke stattdessen die Übersetzungsfunktion verwendet haben. Diese Funktion steht jedem Objekt zur Verfügung, das wir über den Aufruf newImage erstellen. Beide Methoden funktionieren, verwenden Sie also die eine oder andere oder eine Mischung. Eine andere Sache, die Sie beachten sollten, ist, dass zwischen einigen Blöcken eine Lücke besteht. Das geschieht, weil wir die Position der neuen Blöcke direkt neben der alten Position des letzten Blocks festlegen. Das Problem ist, dass wir auf diese Weise versuchen, es neben einem sich bewegenden Objekt zu platzieren, sodass wir möglicherweise nicht immer direkt daneben treffen. Dieses Problem wird noch größer, wenn wir versuchen, die Fahrgeschwindigkeit der Blöcke zu erhöhen. Je schneller sie sich bewegen, desto größer wird die Lücke. Dieses Problem kann überwunden werden, indem einfach eine Geschwindigkeitsvariable hinzugefügt und unsere Berechnungen basierend auf dieser Geschwindigkeit durchgeführt werden. Gehen Sie zurück zu den Codezeilen, in denen wir die Variablen groundMin und groundMax initialisiert haben, und fügen Sie Folgendes hinzu:

1
local speed = 5;

Ersetzen Sie als Nächstes die Funktionen updateBlocks() und updateBackgrounds() durch folgende:

1
function updateBlocks()
2
for a = 1, blocks.numChildren, 1 do
3
4
if(a > 1) then
5
newX = (blocks[a - 1]).x + 79
6
else
7
newX = (blocks[8]).x + 79 - speed
8
end
9
10
if((blocks[a]).x < -40) then
11
(blocks[a]).x, (blocks[a]).y = newX, (blocks[a]).y
12
else
13
(blocks[a]):translate(speed * -1, 0)
14
end
15
16
end
17
end
18
19
function updateBackgrounds()
20
--far background movement
21
backgroundfar.x = backgroundfar.x - (speed/55)
22
23
--near background movement
24
backgroundnear1.x = backgroundnear1.x - (speed/5)
25
if(backgroundnear1.x < -239) then
26
backgroundnear1.x = 760
27
end
28
29
backgroundnear2.x = backgroundnear2.x - (speed/5)
30
if(backgroundnear2.x < -239) then
31
backgroundnear2.x = 760
32
end
33
end

Mit diesen aktualisierten Funktionen sollten sich Ihre Blöcke ohne Lücken gut bewegen können!

Eine letzte Sache, um dies spielerischer zu machen. Gehen Sie zurück in die Update-Funktion und geben Sie diese Codezeile nach den Update-Funktionen ein:

1
speed = speed + .05

Sobald das da drin ist, können Sie sehen, wie sich das Level vor Ihren Augen beschleunigt! Der genaue Wert ist natürlich nur ein Beispiel. Wie schnell Sie das Spiel aktualisieren, hängt von Ihrem Spiel und Ihrem Publikum ab. Alles, was an diesem Punkt übrig bleibt, ist, einen Helden und einige Hindernisse hineinzuwerfen, und wir haben selbst ein Spiel. Das wird jedoch später kommen, da das das Projekt vorerst abschließt. Es war nicht so schlimm, all das zum Laufen zu bringen, und wir werden bei jeder Iteration weiterhin viele Funktionen hinzufügen. Schauen Sie unbedingt für das nächste Tutorial noch einmal vorbei und lassen Sie mich in den Kommentaren unten wissen, wenn Sie Fragen oder Feedback haben!