您的位置:首页 > 移动开发 > Android开发

零打碎敲学Android(四)—跳跃的火之意志!

2009-10-25 09:38 239 查看
动作游戏(ACT),作为在N早以前的红白机时代,便已经大量普及的经典游戏类型,它强调玩家的反应能力和手眼的配合,属于快节奏游戏的典型。相对而言,动作游戏的剧情一般也比较简单,主要通过熟悉操作技巧就可以进行游戏,多以2D为主潮流。在游戏中,玩家控制的角色不仅可以走、跑、跳,甚至可以俯身、爬行、翻滚、飞行、甚至爬墙(见附图:《忍者龙剑传》)。





ACT游戏的武器也多种多样,近程、远程、定时,还可以使用大量的辅助道具,譬如:吊索(例如《蜘蛛侠》)。加之种种场景的地形效果,流沙、冰地、履带、齿轮等等,配合场景中的种种机关,如墙上的开关、灯控、钉板等等,使游戏过程千变万化。即使是3D仿真技术发达的今天,相信仍有不少玩家流连这类ACT游戏吧?

任何一款ACT游戏,从根本上说,都会设定有自己的游戏背景。策划人员在设计这部分内容时,可以充分发挥自己的想象力。游戏的背景可以为数千年前的原始神话时代的《火之鸟》、《彩虹岛》,也可以是神秘奇幻的中世纪《圆桌武士》、《恶魔城》、《波斯王子》,甚至回到雄壮激荡的中国古代,作出《天地吞食》、《杨家将》,乃至于未来时代的《魂斗罗》,或者以著名故事、漫画为原型的《蜘蛛侠》、《梦幻岛》等,乃至于从来没有过的时间、空间、物种。故事需要尽可能的离奇或是感人,这样才能有效地抓住玩家,事实证明,在这样的游戏中,完全可以在游戏过程中加入情节的,譬如《恶魔城·月夜狂想曲》就是一个非常好的例子,在ACT为主的游戏过程中,角色会遇到很多人,为他提供信息,在一定条件下给予其道具,可以中途买卖物品。相信随着游戏的发展,这类多元化游戏将会越来越受欢迎。

而在游戏设计中,操作感也非常重要,矛头直指游戏的上手性。在PC或Android上玩这类游戏,更是对操作要求极高,游戏玩家玩的是否舒服,也全在这一步。举个例子,如我们控制一个角色去跳过一个很宽的沟。步骤应该为:跑——>快跑——>跳跃。初步设定“跑”“加速”“跳跃”必须设到“S”“D”“F”三个键上,通常情况就应该依次把“S”“D”“F”三个相邻的键设为“加速”“跑” “跳跃”。这样,平时玩家在游戏过程中,可以始终按住“D”键不放,需要加速时,无名指按着“S”,过沟大跳跃时,只需要食指点下“F”键就可以了,除此之外,其它的排列都将很别扭,可自己一试。

另外从键盘和鼠标的功能来说,有的东西已经是成规范性的东西了。对应相关的游戏软件我们也可以找到规律,并力图靠近它。做为策划,永远也不要霸道的要求玩家去适应你的规律,找到大众都喜欢都适应的规律才是为策划之正道。

牵扯到ACT游戏,自然免不了会有关卡的概念,所谓关卡就是游戏过程中一个又一个的小节。在这里也是同样可以发挥策划的想象力的好地方,每一关可以设计不同的特色,我想大家一定不会忘记古旧而精典的ACT游戏《魂斗罗》吧,记得履带+下砸钉锤那一关么?记得最后一关会飞起来的毒蝎么?这就是有特色而让人难忘的精典呀。





这一部分可以说是ACT游戏的核心。

至于游戏的规则设定,比如在什么情况下,角色会死亡?在什么情况下,敌NPC会OVER?角色在什么状态下可以躲过敌人飞行道具?角色在情况下会飞行?每一种道具的作用?都是需要设计者费心的,比如有的游戏,在规则上会设计一些独创的内容,如上面提到的《天地吞食》,某角色击打时如果同时按着“下”,就会把击打对象举起来并抛出。

而说起ACT游戏的代表作,那么便首推《超级马里奥》,这款家喻户晓的动作游戏,不但在当时,甚至对后世游戏界产生了无法估量的巨大影响。



截止到2008年,马里奥游戏分别在不同的机种上发布了116种,当中包括马力奥自己为主角的游戏和其他如任天堂大乱斗等游戏上,成为了吉尼斯世界纪录大全中,发行最多款电子游戏的电子游戏角色。



那么,对于这款地球人都知道的游戏,我们可不可以将它移植到Android之上,甚至再加上少许变化,令它成为一款全新的游戏呢?答案是肯定的。



相关知识点说了很多。下面,我将分别给出PC以及Android两个版本的马里奥类游戏实现,让大家对基本业务流程有一个初步了解。



源码下载地址:http://code.google.com/p/loon-simple/downloads/list





PC版实现:



package org.loon.game.test;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.Iterator;
import java.util.LinkedList;
import org.loon.framework.game.simple.GameScene;
import org.loon.framework.game.simple.core.Deploy;
import org.loon.framework.game.simple.core.LTimer;
import org.loon.framework.game.simple.core.LTimerContext;
import org.loon.framework.game.simple.core.Screen;
/**
 * 
 * Copyright 2008 - 2009
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0  * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 * @project loonframework
 * @author chenpeng
 * @email:ceponline@yahoo.com.cn
 * @version 0.1
 */
public class Main extends Screen {
	public static final int WIDTH = 320;
	public static final int HEIGHT = 480;
	private LTimer delay;
	private Map map;
	private Hero hero;
	private ActionKey goLeftKey;
	private ActionKey goRightKey;
	private ActionKey jumpKey;
	private double x, y;
	// 键盘事件记录器
	class ActionKey {
		static final int PRESS_ONLY = 1, STATE_RELEASED = 0, STATE_PRESSED = 1,
				STATE_WAITING_FOR_RELEASE = 2;
		int mode, amount, state;
		public ActionKey() {
			this(0);
		}
		public ActionKey(int mode) {
			this.mode = mode;
			reset();
		}
		public void reset() {
			state = STATE_RELEASED;
			amount = 0;
		}
		public void press() {
			if (state != STATE_WAITING_FOR_RELEASE) {
				amount++;
				state = STATE_PRESSED;
			}
		}
		public void release() {
			state = STATE_RELEASED;
		}
		public boolean isPressed() {
			if (amount != 0) {
				if (state == STATE_RELEASED) {
					amount = 0;
				} else if (mode == PRESS_ONLY) {
					state = STATE_WAITING_FOR_RELEASE;
					amount = 0;
				}
				return true;
			}
			return false;
		}
	}
	public Main() {
		delay = new LTimer(20);
		goLeftKey = new ActionKey();
		goRightKey = new ActionKey();
		jumpKey = new ActionKey(ActionKey.PRESS_ONLY);
		initialize();
	}
	private void initialize() {
		map = new Map("map.txt");
		hero = new Hero("hero.gif", 192, 32, 40, 40, map);
	}
	public void alter(LTimerContext timer) {
		if (delay.action(timer.getTimeSinceLastUpdate())) {
			if (goLeftKey.isPressed()) {
				// 向左移动
				hero.left();
			} else if (goRightKey.isPressed()) {
				// 向右移动
				hero.right();
			} else {
				// 暂停
				hero.stop();
			}
			if (jumpKey.isPressed()) {
				// 跳跃
				hero.jump();
			}
			// 更新角色状态
			hero.update(timer.getTimeSinceLastUpdate());
			// 遍历精灵集合
			LinkedList sprites = map.getSprites();
			for (Iterator it = sprites.iterator(); it.hasNext();) {
				Sprite sprite = (Sprite) it.next();
				// 更新所有精灵状态
				sprite.update(timer.getTimeSinceLastUpdate());
				// 碰撞测试
				if (hero.isCollision(sprite)) {
					// 遇敌
					if (sprite instanceof Enemy) {
						Enemy e = (Enemy) sprite;
						// 踩踏敌人
						if ((int) hero.getY() < (int) e.getY()) {
							sprites.remove(e);
							hero.setForceJump(true);
							hero.jump();
							break;
						} else {
							// 重新开始
							initialize();
						}
						// 食物
					} else if (sprite instanceof Food) {
						Food f = (Food) sprite;
						sprites.remove(f);
						break;
						// 加速宝物
					} else if (sprite instanceof Speed) {
						sprites.remove(sprite);
						Speed speedUp = (Speed) sprite;
						speedUp.use(hero);
						break;
						// 跳跃宝物
					} else if (sprite instanceof Jump) {
						sprites.remove(sprite);
						Jump jump = (Jump) sprite;
						jump.use(hero);
						break;
					}
				}
			}
		}
	}
	/**
	 * 绘制游戏界面
	 */
	public void draw(Graphics2D g) {
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, getWidth(), getHeight());
		int offsetX = Main.WIDTH / 2 - (int) hero.getX();
		offsetX = Math.min(offsetX, 0);
		offsetX = Math.max(offsetX, Main.WIDTH - map.getWidth());
		int offsetY = Main.HEIGHT / 2 - (int) hero.getY();
		offsetY = Math.min(offsetY, 0);
		offsetY = Math.max(offsetY, Main.HEIGHT - map.getHeight());
		map.draw(g, offsetX, offsetY);
		hero.draw(g, offsetX, offsetY);
		LinkedList sprites = map.getSprites();
		for (Iterator it = sprites.iterator(); it.hasNext();) {
			Sprite sprite = (Sprite) it.next();
			x = Map.pixelsToTiles(sprite.getX());
			y = Map.pixelsToTiles(sprite.getY());
			// 精灵绘制限制在可见区域内
			if (x > map.getFirstTileX() && x < map.getLastTileX()
					&& y > map.getFirstTileY() && y < map.getLastTileY()) {
				sprite.draw(g, offsetX, offsetY);
			}
		}
	}
	public void leftClick(MouseEvent arg0) {
	}
	public void middleClick(MouseEvent arg0) {
	}
	public void rightClick(MouseEvent arg0) {
	}
	public void onKey(KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.VK_LEFT) {
			goLeftKey.press();
		}
		if (key == KeyEvent.VK_RIGHT) {
			goRightKey.press();
		}
		if (key == KeyEvent.VK_UP) {
			jumpKey.press();
		}
	}
	public void onKeyUp(KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.VK_LEFT) {
			goLeftKey.release();
		}
		if (key == KeyEvent.VK_RIGHT) {
			goRightKey.release();
		}
		if (key == KeyEvent.VK_UP) {
			jumpKey.release();
		}
	}
	public static void main(String[] args) {
		GameScene frame = new GameScene("火影忍者", WIDTH, HEIGHT);
		Deploy deploy = frame.getDeploy();
		deploy.setScreen(new Main());
		deploy.setShowFPS(true);
		deploy.setLogo(false);
		deploy.setFPS(100);
		deploy.mainLoop();
		frame.showFrame();
	}
}








Android版实现:



package org.loon.framework.android.game.act;
import java.util.Iterator;
import java.util.LinkedList;
import org.loon.framework.android.game.LAGraphics;
import org.loon.framework.android.game.LAScreen;
import org.loon.framework.android.game.LTimerContext;
import android.graphics.Color;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
 * 
 * Copyright 2008 - 2009
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0  *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 *
 * @project loonframework
 * @author chenpeng  
 * @email:ceponline@yahoo.com.cn 
 * @version 0.1
 */
public class ScreenTest3 extends LAScreen {
	public static final int WIDTH = 320;
	public static final int HEIGHT = 480;
	// private LTimer delay;
	private Map map;
	private Hero hero;
	private ActionKey goLeftKey;
	private ActionKey goRightKey;
	private ActionKey jumpKey;
	private double x, y;
	// 键盘事件记录器
	class ActionKey {
		static final int PRESS_ONLY = 1, STATE_RELEASED = 0, STATE_PRESSED = 1,
				STATE_WAITING_FOR_RELEASE = 2;
		int mode, amount, state;
		public ActionKey() {
			this(0);
		}
		public ActionKey(int mode) {
			this.mode = mode;
			reset();
		}
		public void reset() {
			state = STATE_RELEASED;
			amount = 0;
		}
		public void press() {
			if (state != STATE_WAITING_FOR_RELEASE) {
				amount++;
				state = STATE_PRESSED;
			}
		}
		public void release() {
			state = STATE_RELEASED;
		}
		public boolean isPressed() {
			if (amount != 0) {
				if (state == STATE_RELEASED) {
					amount = 0;
				} else if (mode == PRESS_ONLY) {
					state = STATE_WAITING_FOR_RELEASE;
					amount = 0;
				}
				return true;
			}
			return false;
		}
	}
	public ScreenTest3() {
		// delay = new LTimer(20);
		goLeftKey = new ActionKey();
		goRightKey = new ActionKey();
		jumpKey = new ActionKey(ActionKey.PRESS_ONLY);
		initialize();
	}
	private void initialize() {
		map = new Map("map.txt");
		hero = new Hero("hero.gif", 192, 32, 40, 40, map);
	}
	public void alter(LTimerContext timer) {
		// PS:虚拟机速度较慢,实机需要开放此部分,否则动作过快
		// if (delay.action(timer.getTimeSinceLastUpdate())) {
		if (goLeftKey.isPressed()) {
			// 向左移动
			hero.left();
		} else if (goRightKey.isPressed()) {
			// 向右移动
			hero.right();
		} else {
			// 暂停
			hero.stop();
		}
		if (jumpKey.isPressed()) {
			// 跳跃
			hero.jump();
		}
		// 更新角色状态
		hero.update(timer.getTimeSinceLastUpdate());
		// 遍历精灵集合
		LinkedList<Sprite> sprites = map.getSprites();
		Iterator<Sprite> it = sprites.iterator();
		while (it.hasNext()) {
			Sprite sprite = it.next();
			// 更新所有精灵状态
			sprite.update(timer.getTimeSinceLastUpdate());
			// 碰撞测试
			if (hero.isCollision(sprite)) {
				// 遇敌
				if (sprite instanceof Enemy) {
					Enemy e = (Enemy) sprite;
					// 踩踏敌人
					if ((int) hero.getY() < (int) e.getY()) {
						sprites.remove(e);
						hero.setForceJump(true);
						hero.jump();
						break;
					} else {
						// 重新开始
						initialize();
					}
					// 食物
				} else if (sprite instanceof Food) {
					Food f = (Food) sprite;
					sprites.remove(f);
					break;
					// 加速宝物
				} else if (sprite instanceof Speed) {
					sprites.remove(sprite);
					Speed speedUp = (Speed) sprite;
					speedUp.use(hero);
					break;
					// 跳跃宝物
				} else if (sprite instanceof Jump) {
					sprites.remove(sprite);
					Jump jump = (Jump) sprite;
					jump.use(hero);
					break;
				}
			}
		}
		// }
	}
	public void draw(LAGraphics g) {
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, getWidth(), getHeight());
		int offsetX = WIDTH / 2 - (int) hero.getX();
		offsetX = Math.min(offsetX, 0);
		offsetX = Math.max(offsetX, WIDTH - map.getWidth());
		int offsetY = HEIGHT / 2 - (int) hero.getY();
		offsetY = Math.min(offsetY, 0);
		offsetY = Math.max(offsetY, HEIGHT - map.getHeight());
		map.draw(g, offsetX, offsetY);
		hero.draw(g, offsetX, offsetY);
		LinkedList<Sprite> sprites = map.getSprites();
		for (Iterator<Sprite> it = sprites.iterator(); it.hasNext();) {
			Sprite sprite = it.next();
			x = Map.pixelsToTiles(sprite.getX());
			y = Map.pixelsToTiles(sprite.getY());
			// 精灵绘制限制在可见区域内
			if (x > map.getFirstTileX() && x < map.getLastTileX()
					&& y > map.getFirstTileY() && y < map.getLastTileY())
				sprite.draw(g, offsetX, offsetY);
		}
	}
	public boolean onKeyDown(int keyCode, KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.KEYCODE_DPAD_LEFT) {
			goLeftKey.press();
		}
		if (key == KeyEvent.KEYCODE_DPAD_RIGHT) {
			goRightKey.press();
		}
		if (key == KeyEvent.KEYCODE_DPAD_UP) {
			jumpKey.press();
		}
		return false;
	}
	public boolean onKeyUp(int keyCode, KeyEvent e) {
		int key = e.getKeyCode();
		if (key == KeyEvent.KEYCODE_DPAD_LEFT) {
			goLeftKey.release();
		}
		if (key == KeyEvent.KEYCODE_DPAD_RIGHT) {
			goRightKey.release();
		}
		if (key == KeyEvent.KEYCODE_DPAD_UP) {
			jumpKey.release();
		}
		return false;
	}
	public boolean onTouchDown(MotionEvent e) {
		return false;
	}
	public boolean onTouchMove(MotionEvent e) {
		return false;
	}
	public boolean onTouchUp(MotionEvent e) {
		return false;
	}
}








源码下载地址:http://code.google.com/p/loon-simple/downloads/list





——————————————————————————————




搞Java的同僚们,有空想着去http://www.java.com/en/store/intl-contact.jsp
申请2010年开通中国区的商店服务吧,别最后搞得和Android Market似的,只能托人“洗钱”……



这是待发的***G示例截图:





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: