本文概述
- Processing教程:一个简单的游戏
- 构建蓬松乒乓球
- 使用p5.js将Processing游戏代码移植到Web
- Processing游戏代码:你也可以做
在本文中, 我将逐步向你展示如何使用Processing来实现自己的游戏。将详细说明每个步骤。然后, 我们将游戏移植到网络上。
文章图片
在开始Processing教程之前, 这是上一部分DVD徽标练习的代码。如有任何疑问, 请务必发表评论。
Processing教程:一个简单的游戏 我们将在此Processing教程中构建的游戏是Flappy Bird, Pong和Brick Breaker的组合。我之所以选择这样的游戏, 是因为它具有初学者在学习游戏开发时会遇到的大多数概念。这是基于我担任助教以来的经验, 帮助新程序员学习如何使用Processing。这些概念包括重力, 碰撞, 保持分数, Processing不同的屏幕以及键盘/鼠标交互。 Flappy Pong拥有所有这些。
立即玩游戏!
如果不使用面向对象编程(OOP)的概念, 就很难构建复杂的游戏, 例如具有多个级别, 玩家, 实体等的平台游戏。随着我们的前进, 你将看到代码如何迅速变得复杂。我尽我所能使此” Processing” 教程井井有条且简单。
我建议你阅读本文, 获取完整的代码, 自己使用它, 开始尽快考虑自己的游戏, 并开始实施它。
让我们开始吧。
构建蓬松乒乓球 Processing教程步骤1:初始化和Processing不同的屏幕
第一步是初始化我们的项目。对于初学者, 我们将像往常一样编写设置和绘制块, 没有新奇或幻想。然后, 我们将Processing不同的屏幕(初始屏幕, 游戏屏幕, 游戏上方屏幕等)。因此出现了问题, 我们如何使Processing在正确的时间显示正确的页面?
完成此任务非常简单。我们将有一个全局变量, 用于存储当前活动屏幕的信息。然后, 我们根据变量绘制正确屏幕的内容。在绘制块中, 我们将具有一个if语句, 该语句检查变量并相应地显示屏幕内容。每当我们想要更改屏幕时, 我们都会将该变量更改为我们希望其显示的屏幕的标识符。这么说, 这就是我们的框架代码:
/********* VARIABLES *********/// We control which screen is active by settings / updating
// gameScreen variable. We display the correct screen according
// to the value of this variable.
//
// 0: Initial Screen
// 1: Game Screen
// 2: Game-over Screenint gameScreen = 0;
/********* SETUP BLOCK *********/void setup() {
size(500, 500);
}/********* DRAW BLOCK *********/void draw() {
// Display the contents of the current screen
if (gameScreen == 0) {
initScreen();
} else if (gameScreen == 1) {
gameScreen();
} else if (gameScreen == 2) {
gameOverScreen();
}
}/********* SCREEN CONTENTS *********/void initScreen() {
// codes of initial screen
}
void gameScreen() {
// codes of game screen
}
void gameOverScreen() {
// codes for game over screen
}/********* INPUTS *********/public void mousePressed() {
// if we are on the initial screen when clicked, start the game
if (gameScreen==0) {
startGame();
}
}/********* OTHER FUNCTIONS *********/// This method sets the necessary variables to start the game
void startGame() {
gameScreen=1;
}
乍一看, 这似乎很可怕, 但是我们所做的只是建立基本结构, 并用注释框分隔不同的部分。
如你所见, 我们为要显示的每个屏幕定义了不同的方法。在我们的绘制块中, 我们只需检查我们的gameScreen变量的值, 然后调用相应的方法。
在void mousePressed(){… }部分中, 我们正在监听鼠标单击, 如果活动屏幕为0(初始屏幕), 则调用startGame()方法, 该方法将按你期望的方式启动游戏。此方法的第一行将gameScreen变量更改为1, 即游戏屏幕。
如果可以理解, 那么下一步就是实施我们的初始屏幕。为此, 我们将编辑initScreen()方法。它去了:
void initScreen() {
background(0);
textAlign(CENTER);
text("Click to start", height/2, width/2);
}
现在, 我们的初始屏幕具有黑色背景和简单文本, 即” 单击以开始” , 位于中间并与中心对齐。但是, 当我们单击时, 什么也没有发生。我们尚未为游戏屏幕指定任何内容。方法gameScreen()中没有任何内容, 因此我们没有将background()作为第一行绘制内容来覆盖从最后一个屏幕(文本)中绘制的先前内容。这就是为什么文本仍然存在的原因, 即使不再调用text()行(就像最后一部分中留下痕迹的移动球示例一样)。出于相同的原因, 背景仍为黑色。因此, 让我们继续并开始实现游戏屏幕。
void gameScreen() {
background(255);
}
进行此更改后, 你会注意到背景变成白色并且文本消失。
Processing教程步骤2:创建球并实现重力
现在, 我们将开始在游戏屏幕上工作。我们将首先创建我们的球。我们应该为其坐标, 颜色和大小定义变量, 因为稍后可能需要更改这些值。例如, 如果我们想随着球手得分的增加而增加球的大小, 以使比赛变得更加困难。我们将需要更改其大小, 因此它应该是一个变量。在实现重力之后, 我们还将定义球的速度。
首先, 我们添加以下内容:
...
int ballX, ballY;
int ballSize = 20;
int ballColor = color(0);
...
void setup() {
...
ballX=width/4;
ballY=height/5;
}
...
void gameScreen() {
...
drawBall();
}
...
void drawBall() {
fill(ballColor);
ellipse(ballX, ballY, ballSize, ballSize);
}
我们将坐标定义为全局变量, 创建了一个绘制球的方法, 称为gameScreen方法。这里唯一需要注意的是我们初始化了坐标, 但是我们在setup()中定义了它们。我们这样做的原因是我们希望球从左四分之一开始, 从顶部五分之一开始。我们并不需要特别的理由, 但这是开始发球的好方法。因此, 我们需要动态获取草图的宽度和高度。草图大小在第一行之后的setup()中定义。在运行setup()之前未设置width和height, 这就是为什么如果我们在顶部定义变量则无法实现这一点。
重力 现在实现重力实际上是容易的部分。只有几招。首先是实现:
...
float gravity = 1;
float ballSpeedVert = 0;
...
void gameScreen() {
...
applyGravity();
keepInScreen();
}
...
void applyGravity() {
ballSpeedVert += gravity;
ballY += ballSpeedVert;
}
void makeBounceBottom(float surface) {
ballY = surface-(ballSize/2);
ballSpeedVert*=-1;
}
void makeBounceTop(float surface) {
ballY = surface+(ballSize/2);
ballSpeedVert*=-1;
}
// keep ball in the screen
void keepInScreen() {
// ball hits floor
if (ballY+(ballSize/2) >
height) {
makeBounceBottom(height);
}
// ball hits ceiling
if (ballY-(ballSize/2) <
0) {
makeBounceTop(0);
}
}
结果是:
文章图片
抱着你的马, 物理学家。我知道这不是重力在现实生活中的工作方式。相反, 这更多的是动画过程。我们定义为重力的变量只是一个数字值(一个浮点数, 以便我们可以使用十进制值, 而不仅仅是整数), 我们在每个循环中将其添加到ballSpeedVert中。 ballSpeedVert是球的垂直速度, 它在每个循环中被加到球的Y坐标(ballY)上。我们观察球的坐标并确保其停留在屏幕上。如果我们不这样做, 球会掉到无穷远。目前, 我们的球只垂直移动。因此, 我们观察屏幕的地板和天花板边界。使用keepInScreen()方法, 我们检查ballY(+半径)是否小于高度, 并且类似地ballY(-半径)大于0。如果不满足条件, 我们使球反弹(从底部开始)或顶部)与makeBounceBottom()和makeBounceTop()方法。要使球弹起, 我们只需将球移到必须弹起的确切位置, 然后将垂直速度(ballSpeedVert)乘以-1(乘以-1即可更改符号)。当速度值带有负号时, 将Y坐标加起来, 速度将变为ballY +(-ballSpeedVert), 即ballY-ballSpeedVert。因此, 球立即以相同的速度改变方向。然后, 当我们向ballSpeedVert中添加重力并且ballSpeedVert具有负值时, 它开始接近0, 最终变为0, 并再次开始增加。这会使球上升, 上升变慢, 停止并开始下降。
文章图片
但是, 我们的动画Processing过程存在问题-球不断弹跳。如果这是现实情况, 则每次接触表面时, 球都会面临空气阻力和摩擦。这就是我们在游戏动画过程中想要的行为, 因此实现起来很容易。我们添加以下内容:
...
float airfriction = 0.0001;
float friction = 0.1;
...
void applyGravity() {
...
ballSpeedVert -= (ballSpeedVert * airfriction);
}
void makeBounceBottom(int surface) {
...
ballSpeedVert -= (ballSpeedVert * friction);
}
void makeBounceTop(int surface) {
...
ballSpeedVert -= (ballSpeedVert * friction);
}
现在, 我们的动画过程产生了以下结果:
文章图片
顾名思义, 摩擦是表面摩擦, 而空气摩擦是空气的摩擦。因此很明显, 每次球接触任何表面时都必须施加摩擦。然而, 空中摩擦必须不断应用。这就是我们所做的。 applyGravity()方法在每个循环上运行, 因此我们在每个循环上从ballSpeedVert中减去其当前值的0.0001%。当球接触任何表面时, makeBounceBottom()和makeBounceTop()方法将运行。因此, 在这些方法中, 我们只是做了一次相同的事情, 只是这次只是摩擦。
Processing教程步骤3:创建球拍
现在我们需要一个球拍来使球反弹。我们应该控制球拍。让我们用鼠标控制它。这是代码:
...
color racketColor = color(0);
float racketWidth = 100;
float racketHeight = 10;
...
void gameScreen() {
...
drawRacket();
...
}
...
void drawRacket(){
fill(racketColor);
rectMode(CENTER);
rect(mouseX, mouseY, racketWidth, racketHeight);
}
我们将球拍的颜色, 宽度和高度定义为全局变量, 我们可能希望它们在游戏过程中发生变化。我们实现了一个方法drawRacket(), 该方法可以实现其名称所建议的功能。我们将rectMode设置为居中, 因此我们的球拍与光标的中心对齐。
现在我们创建了球拍, 我们必须在球上弹跳。
...
int racketBounceRate = 20;
...
void gameScreen() {
...
watchRacketBounce();
...
}
...
void watchRacketBounce() {
float overhead = mouseY - pmouseY;
if ((ballX+(ballSize/2) >
mouseX-(racketWidth/2)) &
&
(ballX-(ballSize/2) <
mouseX+(racketWidth/2))) {
if (dist(ballX, ballY, ballX, mouseY)<
=(ballSize/2)+abs(overhead)) {
makeBounceBottom(mouseY);
// racket moving up
if (overhead<
0) {
ballY+=overhead;
ballSpeedVert+=overhead;
}
}
}
}
结果如下:
文章图片
所以watchRacketBounce()要做的是确保球拍和球发生碰撞。这里有两件事要检查, 即球和球拍在垂直和水平方向上是否都对齐。第一个if语句检查球右侧的X坐标是否大于球拍左侧的X坐标(反之亦然)。如果是, 则第二条语句检查球和球拍之间的距离是否小于或等于球的半径(这意味着它们正在碰撞)。因此, 如果满足这些条件, 则将调用makeBounceBottom()方法, 并且球会在我们的球拍上反弹(在球拍所在的mouseY处)。
你是否注意到mouseY-pmouseY计算出的可变开销? pmouseX和pmouseY变量将鼠标的坐标存储在前一帧。由于鼠标可以非常快速地移动, 因此如果鼠标足够快地朝着球移动, 我们很有可能无法正确检测到帧之间球与球拍之间的距离。因此, 我们考虑了帧之间鼠标坐标的差异, 并在检测距离时将其考虑在内。鼠标移动得越快, 可接受的距离就越大。
我们还出于其他原因使用开销。我们通过检查开销的迹象来检测鼠标的移动方式。如果开销为负, 则鼠标位于前一帧的下方, 因此我们的鼠标(球拍)正在向上移动。在这种情况下, 我们希望为球增加一个额外的速度, 并将其移动到比常规弹跳稍远的位置, 以模拟用球拍击球的效果。如果开销小于0, 则将其添加到ballY和ballSpeedVert中, 以使球变得更高, 更快。因此, 球拍击球的速度越快, 它上升的速度就越高。
Processing教程步骤4:水平移动和控制球
在本节中, 我们将向球添加水平运动。然后, 我们将可以用球拍水平控制球。开始了:
...
// we will start with 0, but for we give 10 just for testing
float ballSpeedHorizon = 10;
...
void gameScreen() {
...
applyHorizontalSpeed();
...
}
...
void applyHorizontalSpeed(){
ballX += ballSpeedHorizon;
ballSpeedHorizon -= (ballSpeedHorizon * airfriction);
}
void makeBounceLeft(float surface){
ballX = surface+(ballSize/2);
ballSpeedHorizon*=-1;
ballSpeedHorizon -= (ballSpeedHorizon * friction);
}
void makeBounceRight(float surface){
ballX = surface-(ballSize/2);
ballSpeedHorizon*=-1;
ballSpeedHorizon -= (ballSpeedHorizon * friction);
}
...
void keepInScreen() {
...
if (ballX-(ballSize/2) <
0){
makeBounceLeft(0);
}
if (ballX+(ballSize/2) >
width){
makeBounceRight(width);
}
}
结果是:
文章图片
这里的想法与我们进行垂直运动的想法相同。我们创建了一个水平速度变量ballSpeedHorizo??n。我们创建了一种将水平速度应用于ballX并消除空气摩擦的方法。我们向keepInScreen()方法添加了两个if语句, 该语句将观察球触及屏幕的左边缘和右边缘。最后, 我们创建了makeBounceLeft()和makeBounceRight()方法来Processing左右反弹。
现在我们为游戏增加了水平速度, 我们想用球拍控制球。就像在著名的Atari游戏Breakout和其他所有打砖块游戏中一样, 球应该根据球拍击中的点向左或向右移动。球拍的边缘应使球具有更高的水平速度, 而中间不应该有任何作用。代码优先:
void watchRacketBounce() {
...
if ((ballX+(ballSize/2) >
mouseX-(racketWidth/2)) &
&
(ballX-(ballSize/2) <
mouseX+(racketWidth/2))) {
if (dist(ballX, ballY, ballX, mouseY)<
=(ballSize/2)+abs(overhead)) {
...
ballSpeedHorizon = (ballX - mouseX)/5;
...
}
}
}
结果是:
文章图片
将该简单的行添加到watchRacketBounce()即可完成工作。我们要做的是使用ballX-mouseX确定球击中球拍中心的点的距离。然后, 将其设为水平速度。实际的差异太大, 因此我尝试了一下, 并认为十分之一的值是最自然的。
Processing教程步骤5:创建墙
我们的草图开始看起来更像游戏中的每一步。在这一步中, 我们将添加向左移动的墙, 就像在《飞扬的小鸟》中一样:
...
int wallSpeed = 5;
int wallInterval = 1000;
float lastAddTime = 0;
int minGapHeight = 200;
int maxGapHeight = 300;
int wallWidth = 80;
color wallColors = color(0);
// This arraylist stores data of the gaps between the walls. Actuals walls are drawn accordingly.
// [gapWallX, gapWallY, gapWallWidth, gapWallHeight]
ArrayList<
int[]>
walls = new ArrayList<
int[]>
();
...
void gameScreen() {
...
wallAdder();
wallHandler();
}
...
void wallAdder() {
if (millis()-lastAddTime >
wallInterval) {
int randHeight = round(random(minGapHeight, maxGapHeight));
int randY = round(random(0, height-randHeight));
// {gapWallX, gapWallY, gapWallWidth, gapWallHeight}
int[] randWall = {width, randY, wallWidth, randHeight};
walls.add(randWall);
lastAddTime = millis();
}
}
void wallHandler() {
for (int i = 0;
i <
walls.size();
i++) {
wallRemover(i);
wallMover(i);
wallDrawer(i);
}
}
void wallDrawer(int index) {
int[] wall = walls.get(index);
// get gap wall settings
int gapWallX = wall[0];
int gapWallY = wall[1];
int gapWallWidth = wall[2];
int gapWallHeight = wall[3];
// draw actual walls
rectMode(CORNER);
fill(wallColors);
rect(gapWallX, 0, gapWallWidth, gapWallY);
rect(gapWallX, gapWallY+gapWallHeight, gapWallWidth, height-(gapWallY+gapWallHeight));
}
void wallMover(int index) {
int[] wall = walls.get(index);
wall[0] -= wallSpeed;
}
void wallRemover(int index) {
int[] wall = walls.get(index);
if (wall[0]+wall[2] <
= 0) {
walls.remove(index);
}
}
结果是:
文章图片
即使代码看起来冗长而令人生畏, 我保证没有什么很难理解的。首先要注意的是ArrayList。对于那些不知道ArrayList是什么的人来说, 它只是list的实现, 就像Array一样, 但是相对于它, 它有一些优势。它是可调整大小的, 具有有用的方法, 如list.add(index), list.get(index)和list.remove(index)。我们将墙数据保留为arraylist中的整数数组。我们保留在数组中的数据用于两堵墙之间的间隙。数组包含以下值:
[gap wall X, gap wall Y, gap wall width, gap wall height]
实际壁是根据间隙壁值绘制的。请注意, 使用类可以更好, 更简洁地Processing所有这些问题, 但是由于” 面向对象编程” (OOP)的使用不在本” Processing” 教程的范围之内, 因此我们将采用这种方式进行Processing。我们有两种管理墙壁的基本方法, wallAdder()和wallHandler。
wallAdder()方法仅在每个wallInterval毫秒内将新的墙添加到arraylist中。我们有一个全局变量lastAddTime, 它存储添加最后一堵墙的时间(以毫秒为单位)。如果当前毫秒millis()减去最后添加的毫秒lastAddTime大于我们的间隔值wallInterval, 则意味着现在该添加一个新的墙了。然后根据最顶部定义的全局变量生成随机间隙变量。然后将新的墙(存储间隙墙数据的整数数组)添加到arraylist中, 并将lastAddTime设置为当前毫秒millis()。
wallHandler()循环遍历arraylist中的当前墙。对于每个循环中的每个项目, 它通过arraylist的索引值调用wallRemover(i), wallMover(i)和wallDrawer(i)。这些方法如其名称所示。 wallDrawer()根据间隙壁数据绘制实际的壁。它从arraylist中获取墙数据数组, 并调用rect()方法将墙绘制到实际位置。 wallMover()方法从arraylist中获取元素, 并根据wallSpeed全局变量更改其X位置。最后, wallRemover()从arraylist中移除屏幕之外的墙。如果我们不这样做, 那么Processing程序将把它们视为仍在屏幕中。那将是巨大的性能损失。因此, 当从阵列列表中删除墙时, 它不会在后续循环中绘制。
最后要做的挑战性工作是检测球与墙壁之间的碰撞。
void wallHandler() {
for (int i = 0;
i <
walls.size();
i++) {
...
watchWallCollision(i);
}
}
...
void watchWallCollision(int index) {
int[] wall = walls.get(index);
// get gap wall settings
int gapWallX = wall[0];
int gapWallY = wall[1];
int gapWallWidth = wall[2];
int gapWallHeight = wall[3];
int wallTopX = gapWallX;
int wallTopY = 0;
int wallTopWidth = gapWallWidth;
int wallTopHeight = gapWallY;
int wallBottomX = gapWallX;
int wallBottomY = gapWallY+gapWallHeight;
int wallBottomWidth = gapWallWidth;
int wallBottomHeight = height-(gapWallY+gapWallHeight);
if (
(ballX+(ballSize/2)>
wallTopX) &
&
(ballX-(ballSize/2)<
wallTopX+wallTopWidth) &
&
(ballY+(ballSize/2)>
wallTopY) &
&
(ballY-(ballSize/2)<
wallTopY+wallTopHeight)
) {
// collides with upper wall
}if (
(ballX+(ballSize/2)>
wallBottomX) &
&
(ballX-(ballSize/2)<
wallBottomX+wallBottomWidth) &
&
(ballY+(ballSize/2)>
wallBottomY) &
&
(ballY-(ballSize/2)<
wallBottomY+wallBottomHeight)
) {
// collides with lower wall
}
}
watchWallCollision()方法在每个循环中的每个墙都被调用。我们获取间隙壁的坐标, 计算实际壁(顶部和底部)的坐标, 然后检查球的坐标是否与壁碰撞。
Processing教程步骤6:运行状况和评分
现在我们可以检测到球和墙壁的碰撞, 我们可以决定游戏的机制了。在对游戏进行了一些调整之后, 我设法使游戏具有一定的可玩性。但是, 这仍然非常困难。我对游戏的第一个想法是使它像《飞扬的小鸟》, 当球碰到墙壁时, 游戏结束。但是后来我意识到这是不可能的。所以这就是我的想法:
球顶应该有一个健康栏。球碰壁时应该失去健康。按照这种逻辑, 让球从墙壁弹回是没有意义的。因此, 当运行状况为0时, 游戏应该结束, 并且我们应该通过屏幕切换到游戏。所以我们开始:
int maxHealth = 100;
float health = 100;
float healthDecrease = 1;
int healthBarWidth = 60;
...
void gameScreen() {
...
drawHealthBar();
...
}
...
void drawHealthBar() {
// Make it borderless:
noStroke();
fill(236, 240, 241);
rectMode(CORNER);
rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth, 5);
if (health >
60) {
fill(46, 204, 113);
} else if (health >
30) {
fill(230, 126, 34);
} else {
fill(231, 76, 60);
}
rectMode(CORNER);
rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth*(health/maxHealth), 5);
}
void decreaseHealth(){
health -= healthDecrease;
if (health <
= 0){
gameOver();
}
}
这是一个简单的运行:
文章图片
我们创建了一个全局变量健康状况, 以保持健康状态。然后创建方法drawHealthBar()在球的顶部绘制两个矩形。第一个是基本运行状况栏, 第二个是显示当前运行状况的活动栏。第二个控件的宽度是动态的, 并使用healthBarWidth *(health / maxHealth)(我们当前的健康状况相对于健康条宽度的比率)进行计算。最后, 根据健康值设置填充颜色。最后但并非最不重要的是, 得分:
...
void gameOverScreen() {
background(0);
textAlign(CENTER);
fill(255);
textSize(30);
text("Game Over", height/2, width/2 - 20);
textSize(15);
text("Click to Restart", height/2, width/2 + 10);
}
...
void wallAdder() {
if (millis()-lastAddTime >
wallInterval) {
...
// added another value at the end of the array
int[] randWall = {width, randY, wallWidth, randHeight, 0};
...
}
}
void watchWallCollision(int index) {
...
int wallScored = wall[4];
...
if (ballX >
gapWallX+(gapWallWidth/2) &
&
wallScored==0) {
wallScored=1;
wall[4]=1;
score();
}
}
void score() {
score++;
}
void printScore(){
textAlign(CENTER);
fill(0);
textSize(30);
text(score, height/2, 50);
}
我们需要在球经过墙时得分。但是我们需要为每堵墙添加最大1分。意思是, 如果球越过一堵墙然后又越过一堵墙, 则不应添加其他得分。为了实现这一点, 我们在arraylist的间隙壁数组中添加了另一个变量。如果球还没有越过那堵墙, 新变量将存储0;如果球还没有越过该墙, 则新变量将存储1。然后, 我们修改了watchWallCollision()方法。我们添加了一个条件, 当球通过之前从未通过的墙时, 它会触发score()方法并将墙标记为已通过。
我们现在快结束了。最后要做的是在屏幕上的游戏上单击重新启动。我们需要将所有使用过的变量设置为其初始值, 然后重新启动游戏。这里是:
...
public void mousePressed() {
...
if (gameScreen==2){
restart();
}
}
...
void restart() {
score = 0;
health = maxHealth;
ballX=width/4;
ballY=height/5;
lastAddTime = 0;
walls.clear();
gameScreen = 0;
}
让我们添加更多颜色。
文章图片
瞧!我们有蓬松的乒乓球!
完整的《 Processing》游戏代码可在此处找到。
使用p5.js将Processing游戏代码移植到Web p5.j??s是一个JavaScript库, 其语法与Processing编程语言非常相似。它不是一个能够简单运行现有Processing代码的库;相反, p5.js需要编写实际的JavaScript代码-类似于Processing的JavaScript端口, 即Processing.js。我们的任务是使用p5.js API将Processing代码转换为JavaScript。该库具有一组类似于Processing的函数和语法, 并且我们必须对代码进行某些更改才能使其在JavaScript中工作-但由于Processing和JavaScript与Java都具有相似之处, 因此这听起来并不容易。 。即使你不是JavaScript开发人员, 所做的更改也是微不足道的, 你应该可以很好地遵循。
首先, 我们需要创建一个简单的index.html并将p5.min.js添加到我们的标头中。我们还需要创建另一个名为flappy_pong.js的文件, 该文件将存储我们转换后的代码。
<
html>
<
head>
<
title>
Flappy Pong<
/title>
<
script tyle="text/javascript" src="http://img.readke.com/220519/0F02TX1-10.jpg">
<
/script>
<
script tyle="text/javascript" src="http://www.srcmini.com/flappy_pong.js">
<
/script>
<
style>
canvas {
box-shadow: 0 0 20px lightgray;
}
<
/style>
<
/head>
<
body>
<
/body>
<
/html>
转换代码时, 我们的策略应该是将所有代码复制并粘贴到flippy_pong.js中, 然后进行所有更改。这就是我所做的。这是我更新代码所采取的步骤:
Javascript是一种无类型的语言(没有int和float这样的类型声明)。因此, 我们需要将所有类型声明更改为var。
Javascript中没有空白。我们应该改变所有功能。
我们需要从函数签名中删除参数的类型声明。 (即void wallMover(var index){to function wallMover(index){)
JavaScript中没有ArrayList。但是我们可以使用JavaScript数组实现相同的目的。我们进行以下更改:
- ArrayList < int []> 墙=新的ArrayList < int []> (); 到var墙= [];
- wall.clear(); 到墙壁= [];
- wall.add(randWall); 到walls.push(randWall);
- wall.remove(index); 到wall.splice(index, 1);
- wall.get(index); 到墙壁[索引]
- walls.size()到walls.length
删除所有公共关键字。
将所有color(0)声明移到函数setup()中, 因为在调用setup()之前不会定义color()。
更改大小(500, 500); 创建canvas(500, 500);
将函数gameScreen(){重命名为其他函数, 例如gamePlayScreen(){, 因为我们已经有一个名为gameScreen的全局变量。当我们使用Processing时, 一个是函数, 另一个是int变量。但是JavaScript会混淆这些类型, 因为它们没有类型。
Score()也一样。我将其重命名为addScore()。
可以在此处找到涵盖此Processing教程中所有内容的完整JavaScript代码。
Processing游戏代码:你也可以做 在此Processing教程中, 我试图解释如何构建一个非常简单的游戏。但是, 我们在本文中所做的只是冰山一角。使用Processing编程语言, 几乎可以实现任何目的。我认为, 这是编程你所想象的最佳工具。我使用此Processing教程的真正目的不是要讲授Processing和开发游戏, 以证明编程并不难。打造自己的游戏不仅仅是梦想。我想向你展示, 只要稍加努力和热情, 你就可以做到。我真的希望这两篇文章能激励大家尝试编程。
推荐阅读
- 用PeerJS驯服WebRTC(制作一个简单的P2P网络游戏)
- Processing语言终极指南第一部分(基础知识)
- 单一责任原则(伟大准则的秘诀)
- 使用Bootstrap加速应用程序开发
- 软件再造(从意大利面到干净的设计)
- Sass样式指南(有关如何编写更好的CSS代码的Sass教程)
- 如何使用Tornado创建一个简单的Python WebSocket服务器
- 使用Java和Spring Security的JWT实现REST安全性
- 使用Google App Engine GQL查找给定半径内的所有位置