【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解

 

Snake也是一个经典游戏了,Nokia蓝屏机的王牌游戏之一。Android SDK
1.5就有了它的身影。我们这里就来详细解析一下Android
SDK Sample中的Snake工程。本工程基于SDK 2.3.3版本中的工程,路径为:%Android_SDK_HOME%
/samples/android-10/Snake

一、Eclipse工程

 通过File-New
Project-Android-Android Project,选择“Create project from
existing sample”创建自己的应用SnakeAndroid,如下图:

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />

 

运行效果如下图:

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />
 

二、工程结构和类图

 其实Snake的工程蛮简单的,源文件就三个:Snake.java
SnakeView.java TileView.javaSnake类是这个游戏的入口点,TitleView类进行游戏的绘画,SnakeView类则是对游戏控制操作的处理。CoordinateRefreshHandler2个辅助类,也是SnakeView类中的内部类。其中,Coordinate是一个点的坐标(xy),RefreshHandlerRefreshHandler对象绑定某个线程并给它发送消息。如下图:

 

 【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />

任何游戏都需要有个引擎来推动游戏的运行,最简化的游戏引擎就是:在一个线程中While循环,检测用户操作,对用户的操作作出反应,更新游戏的界面,直到用户退出游戏。

Snake这个游戏中,辅助类RefreshHandler继承自Handler,用来把RefreshHandler与当前线程进行绑定,从而可以直接给线程发送消息并处理消息。注意一点:Handle对消息的处理都是异步。RefreshHandlerHandler的基础上增加sleep()接口,用来每隔一个时间段后给当前线程发送一个消息。handleMessage()方法在接受消息后,根据当前的游戏状态重绘界面,运行机制如下:

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />
 

这比较类似定时器的概念,在特定的时刻发送消息,根据消息处理相应的事件。update()sleep()间接的相互调用就构成了一个循环。这里要注意:mRedrawHandle绑定的是Avtivity所在的线程,也就是程序的主线程;另外由于sleep()是个异步函数,所以update()sleep()之间的相互调用才没有构成死循环。

最后分析下游戏数据的保存机制,如下:

【贪吃蛇—Java程序员写Android游戏】系列 <wbr>1.Android <wbr>SDK <wbr>Sample-Snake详解1.Android SDK Sample-Snake详解” TITLE=”【贪吃蛇—Java程序员写Android游戏】系列 1.Android SDK Sample-Snake详解” />

 

这里考虑了Activity的生命周期:如果用户在游戏期间离开游戏界面,游戏暂停;或者由于内存比较紧张,Android关闭游戏释放内存,那么当用户返回游戏界面的时候恢复到上次离开时的界面。

 

三、源码解析

 

详细解析下源代码,由于代码量不大,以注释的方式列出如下:

1、Snake.java

 

package com.deaboway.snake;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

// 贪吃蛇: 经典游戏,在一个花园中找苹果吃,吃了苹果会变长,速度变快。碰到自己和墙就挂掉。
public class Snake extends Activity {

    private
SnakeView mSnakeView;

    private
static String ICICLE_KEY = “snake-view”;

    
    // 在
activity 第一次创建时被调用
  
 @Override
    public void
onCreate(Bundle savedInstanceState) {

  
   
 super.onCreate(savedInstanceState);
  
   
 setContentView(R.layout.snake_layout);

  
   
 mSnakeView = (SnakeView)
findViewById(R.id.snake);
  
   
 mSnakeView.setTextView((TextView)
findViewById(R.id.text));

  
   
 // 检查存贮状态以确定是重新开始还是恢复状态
  
   
 if (savedInstanceState == null) {
  
   
   
 // 存储状态为空,说明刚启动可以切换到准备状态
  
   
   
 mSnakeView.setMode(SnakeView.READY);
  
   
 } else {
  
   
   
 // 已经保存过,那么就去恢复原有状态
  
   
   
 Bundle map =
savedInstanceState.getBundle(ICICLE_KEY);
  
   
   
 if (map != null) {
  
   
   
   
 // 恢复状态
  
   
   
   
 mSnakeView.restoreState(map);
  
   
   
 } else {
  
   
   
   
 // 设置状态为暂停
  
   
   
   
 mSnakeView.setMode(SnakeView.PAUSE);
  
   
   
 }
  
   
 }
    }

    //
暂停事件被触发时
  
 @Override
    protected
void onPause() {
  
   
 super.onPause();
  
   
 // Pause the game along with the activity
  
   
 mSnakeView.setMode(SnakeView.PAUSE);
    }

    //
状态保存
  
 @Override
    public void
onSaveInstanceState(Bundle outState) {
  
   
 // 存储游戏状态到View里
  
   
 outState.putBundle(ICICLE_KEY,
mSnakeView.saveState());
    }

}

 

2、SnakeView.java

 

package com.deaboway.snake;

import java.util.ArrayList;
import java.util.Random;

import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;

public class SnakeView extends TileView {

    private
static final String TAG = “Deaboway”;

    
    //
游戏状态,默认值是准备状态
    private int
mMode = READY;

    // 游戏的四个状态
暂停 准备 运行 和 失败
    public
static final int PAUSE = 0;
    public
static final int READY = 1;
    public
static final int RUNNING = 2;
    public
static final int LOSE = 3;

    //
游戏中蛇的前进方向,默认值北方
    private int
mDirection = NORTH;
    //
下一步的移动方向,默认值北方
    private int
mNextDirection = NORTH;

    // 游戏方向设定 北
南 东 西
    private
static final int NORTH = 1;
    private
static final int SOUTH = 2;
    private
static final int EAST = 3;
    private
static final int WEST = 4;

    
    //
三种游戏元
    private
static final int RED_STAR = 1;
    private
static final int YELLOW_STAR = 2;
    private
static final int GREEN_STAR = 3;

    
    //
游戏得分
    private long
mScore = 0;

    //
移动延迟
    private long
mMoveDelay = 600;

    
    //
最后一次移动时的毫秒时刻
    private long
mLastMove;

    
    //
显示游戏状态的文本组件
    private
TextView mStatusText;

    
    //
蛇身数组(数组以坐标对象为元素)
    private
ArrayList<Coordinate> mSnakeTrail =
new ArrayList<Coordinate>();

    //
苹果数组(数组以坐标对象为元素)
    private
ArrayList<Coordinate> mAppleList =
new ArrayList<Coordinate>();

    
    // 随机数
    private
static final Random RNG = new Random();

    
    //
创建一个Refresh Handler来产生动画: 通过sleep()来实现
    private
RefreshHandler mRedrawHandler = new RefreshHandler();

    //
一个Handler
    class
RefreshHandler extends Handler {

  
   
 // 处理消息队列
  
   
 @Override
  
   
 public void handleMessage(Message msg) {
  
   
   
 // 更新View对象
  
   
   
 SnakeView.this.update();
  
   
   
 // 强制重绘
  
   
   
 SnakeView.this.invalidate();
  
   
 }

  
   
 // 延迟发送消息
  
   
 public void sleep(long delayMillis) {
  
   
   
 this.removeMessages(0);
  
   
   
 sendMessageDelayed(obtainMessage(0),
delayMillis);
  
   
 }
    };

    
    //
构造函数
    public
SnakeView(Context context, AttributeSet attrs) {
  
   
 super(context, attrs);
  
   
 // 构造时初始化
  
   
 initSnakeView();
    }

    public
SnakeView(Context context, AttributeSet attrs, int defStyle)
{
  
   
 super(context, attrs, defStyle);
  
   
 initSnakeView();
    }

    // 初始化
    private void
initSnakeView() {
  
   
 // 可选焦点
  
   
 setFocusable(true);

  
   
 Resources r =
this.getContext().getResources();

  
   
 // 设置贴片图片数组
  
   
 resetTiles(4);

  
   
 // 把三种图片存到Bitmap对象数组
  
   
 loadTile(RED_STAR,
r.getDrawable(R.drawable.redstar));
  
   
 loadTile(YELLOW_STAR,
r.getDrawable(R.drawable.yellowstar));
  
   
 loadTile(GREEN_STAR,
r.getDrawable(R.drawable.greenstar));

    }

    //
开始新的游戏——初始化
    private void
initNewGame() {
  
   
 // 清空ArrayList列表
  
   
 mSnakeTrail.clear();
  
   
 mAppleList.clear();

  
   
 // For now we’re just going to load up a short
default eastbound snake
  
   
 // that’s just turned north
  
   
 // 创建蛇身

  
   
 mSnakeTrail.add(new Coordinate(7, 7));
  
   
 mSnakeTrail.add(new Coordinate(6, 7));
  
   
 mSnakeTrail.add(new Coordinate(5, 7));
  
   
 mSnakeTrail.add(new Coordinate(4, 7));
  
   
 mSnakeTrail.add(new Coordinate(3, 7));
  
   
 mSnakeTrail.add(new Coordinate(2, 7));

  
   
 // 新的方向 :北方
  
   
 mNextDirection = NORTH;

  
   
 // 2个随机位置的苹果
  
   
 addRandomApple();
  
   
 addRandomApple();

  
   
 // 移动延迟
  
   
 mMoveDelay = 600;
  
   
 // 初始得分0
  
   
 mScore = 0;
    }

 

3、TileView.java

 

package com.deaboway.snake;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

// View 变种,用来处理 一组 贴片—— “icons”或其它可绘制的对象
public class TileView extends View {

    

    protected
static int mTileSize;

    //
X轴的贴片数量
    protected
static int mXTileCount;
    //
Y轴的贴片数量
    protected
static int mYTileCount;

    //
X偏移量
    private
static int mXOffset;
    //
Y偏移量
    private
static int mYOffset;

    
    //
贴片图像的图像数组
    private
Bitmap[] mTileArray;

    
    //
保存每个贴片的索引——二维数组
    private
int[][] mTileGrid;

    //
Paint对象(画笔、颜料)
    private
final Paint mPaint = new Paint();

    //
构造函数
    public
TileView(Context context, AttributeSet attrs, int defStyle) {
  
   
 super(context, attrs, defStyle);

  
   
 TypedArray a =
context.obtainStyledAttributes(attrs,
  
   
   
   
 R.styleable.TileView);

  
   
 mTileSize =
a.getInt(R.styleable.TileView_tileSize, 12);

  
   
 a.recycle();
    }

    public
TileView(Context context, AttributeSet attrs) {
  
   
 super(context, attrs);

  
   
 TypedArray a =
context.obtainStyledAttributes(attrs,
  
   
   
   
 R.styleable.TileView);

  
   
 mTileSize =
a.getInt(R.styleable.TileView_tileSize, 12);

  
   
 a.recycle();
    }

    
    //
设置贴片图片数组
    public void
resetTiles(int tilecount) {
  
   
 mTileArray = new Bitmap[tilecount];
    }

    //
回调:当该View的尺寸改变时调用,在onDraw()方法调用之前就会被调用,所以用来设置一些变量的初始值
    //
在视图大小改变的时候调用,比如说手机由垂直旋转为水平
  
 @Override
    protected
void onSizeChanged(int w, int h, int oldw, int oldh) {

  
   
 // 定义X轴贴片数量
  
   
 mXTileCount = (int) Math.floor(w /
mTileSize);
  
   
 mYTileCount = (int) Math.floor(h /
mTileSize);

  
   
 // X轴偏移量
  
   
 mXOffset = ((w – (mTileSize * mXTileCount)) /
2);

  
   
 // Y轴偏移量
  
   
 mYOffset = ((h – (mTileSize * mYTileCount)) /
2);

  
   
 // 定义贴片的二维数组
  
   
 mTileGrid = new
int[mXTileCount][mYTileCount];

  
   
 // 清空所有贴片
  
   
 clearTiles();
    }

    
    //
给mTileArray这个Bitmap图片数组设置值
    public void
loadTile(int key, Drawable tile) {
  
   
 Bitmap bitmap = Bitmap.createBitmap(mTileSize,
mTileSize,
  
   
   
   
 Bitmap.Config.ARGB_8888);
  
   
 Canvas canvas = new Canvas(bitmap);
  
   
 tile.setBounds(0, 0, mTileSize, mTileSize);
  
   
 // 把一个drawable转成一个Bitmap
  
   
 tile.draw(canvas);
  
   
 // 在数组里存入该Bitmap
  
   
 mTileArray[key] = bitmap;
    }

    
    //
清空所有贴片
    public void
clearTiles() {
  
   
 for (int x = 0; x < mXTileCount;
x++) {
  
   
   
 for (int y = 0; y < mYTileCount;
y++) {
  
   
   
   
 // 全部设置为0
  
   
   
   
 setTile(0, x, y);
  
   
   
 }
  
   
 }
    }

    
    //
给某个贴片位置设置一个状态索引
    public void
setTile(int tileindex, int x, int y) {
  
   
 mTileGrid[x][y] = tileindex;
    }

    // onDraw
在视图需要重画的时候调用,比如说使用invalidate刷新界面上的某个矩形区域
  
 @Override
    public void
onDraw(Canvas canvas) {

  
   
 super.onDraw(canvas);
  
   
 for (int x = 0; x < mXTileCount; x
+= 1) {
  
   
   
 for (int y = 0; y < mYTileCount; y
+= 1) {
  
   
   
   
 // 当索引大于零,也就是不空时
  
   
   
   
 if (mTileGrid[x][y] > 0) {
  
   
   
   
   
 // mTileGrid中不为零时画此贴片
  
   
   
   
   
 canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
mXOffset + x
  
   
   
   
   
   
   
 * mTileSize, mYOffset + y * mTileSize,
mPaint);
  
   
   
   
 }
  
   
   
 }
  
   
 }

    }
}

 

四、工程文件下载

 为了方便大家阅读,可以到如下地址下载工程源代码:

  http://ishare.iask.sina.com.cn/f/14312223.html

五、小结及下期预告:

 本次详细解析了Android SDK
自带 Sample——Snake的结构和功能。下次将会把这个游戏移植到J2ME平台上,并且比较AndroidJ2ME的区别和相通之处,让从事过J2ME开发的朋友对Android开发有个更加直观的认识。

转帖:相信谁—日本遭受地震海啸和随之而来的核电站危机

日本遭受地震海啸和随之而来的核电站危机,颇受世界瞩目,和利比亚事件一起,连续一周占据了全球媒体的头版头条,那边的地震刚刚告一段落,这边就看到网上的帖子开始对“日本人的下场”表示幸灾乐祸,于是不意外地料到这次事件在中国定会引起“爱国主义”的人们和“大爱无疆”人们的一场论战。但没料到的是,事情会在上周末演变成一场莫名其妙的抢盐闹剧,随后的是政府的辟谣,幕后黑手的查出等等相关新闻,然后是海底核试验的说法满天飞,然后是媒体的辟谣,没完没了,又似曾相识,惊诧之余,不禁对常识之缺乏,想象力之丰富,和信任的完全缺失而感叹。
不禁想起,上华盛顿大学EMBA的第一节课,是一个源自美国的危机处理模拟练习,班上同学分为七组,危机的背景是一样的:大家乘坐的飞机在沙漠中坠毁,坠毁前不久某组员曾留意到飞机刚飞过一个小镇,幸存者只有自己的组员,手里有着有限的物资:食物,水,汽油,化妆包,帆布,各种工具等若干。
模拟要求每组幸存者讨论并实施如何生存和获救。通过一小时的激烈争论后,每小组推选自己的领袖发表自己组的求生方案,包括如何善用有限的物资等。发表完后,由教授根据沙漠救援专家的指示发表意见,评估各项措施的得与失,比如,化妆包中的小镜子可用作向空中反射阳光来指示位置求救,或是白天必须用帆布覆盖身体以求活命等等。七组中,有三组主张离开原地向疑似有人烟的方向进发求救;四组主张呆在飞机残骸原地等待救援。教授的评判是,所有在烈日下走开求救的方法都是死路一条,原地保存实力才是正确答案。可想而知,这样的评判本身也遭遇了质疑,教授只能拿出救援专家的详解来平息这场争论。有趣的是,大家在争论结束后,竟然发现三个主张离开求救的组,他们的组长都是中国人,他们的意见在组里占上风;而四个得高分的组,无一例外的,都是老外话事的组,包括一个美国人,一个ABC,一个德国人和一个日本人。
上完课中午吃饭的时候大家还意犹未尽地在讨论,一个中国同学忽然一语中的地说:这样迥然不同的自救方法完全是来自于文化差异,在大多数老外的心目中,早早植根了若有危险,保持镇定,很快会有人来救援的信心的种子,这种信心,来源于对政府,对社团,对专家,对其他人的信任。而中国的同学们,潜意识里早早就没有指望别人,也不相信会有人快速来救,这样信任感高度缺失的情况下,自救是上上之选,那么纷纷走上沙漠中自取灭亡之路也就一点都不奇怪了。而案例的评判者所依据的,也是美国的生存法则,如果换做中国专家,或许会有完全相反的建议和评判了。大家听完,细想之下,都纷纷称是。
其实中国人现在的极端缺乏信任感并不只体现在危机之前,就是在日常生活的办公室里,又有多少人敢靠上司?靠下属?还有靠同事?激烈的竞争,僧多粥少的焦虑和道德底线的越来越低,令到大部分人不敢靠人,也不想被人靠,恶性循环之下,大家都选择了只敢靠自己。我的一个美国同事,就频频向我抱怨他们在中国的公司,就总是公司政治气氛浓郁,人和人之间合作的少,猜疑的多。
记得两年前有个旧段子,说的是:看完《色戒》,觉得女人不可靠;看完《苹果》,觉得老婆不可靠,看完《投名状》,觉得兄弟不可靠;看完《集结号》,觉得组织不可靠;看来看去,还是自己最可靠。不知道是这些电影反映了这些个“不可靠”,还是这些电影,洗脑了人们觉得什么都不可靠。而最后这貌似坚强的“靠自己”背后,又隐藏了多少的孤独和无奈?

php5配置与IIS中isapi筛选器不能加载PHP的解决办法

在WINDOWS 2003 SP2
中安装PHP就遇到了问题,IIS里面ISAPI加载不了,在isapi筛选器里添加PHP后重启IIS就不能访问网站了,打开网页就显示service
unavailable
打开ISAPI筛选器发现PHP是一个向下的红色箭头,说明没有加载。找了很久的原因,最后才发现是权限的问题。

第一步:把php-5.0.0-Win32.zip解压放到C:\php

第二步:php.ini-dist改名为php.ini,查找 ./ 找到第438行改成 extension_dir =
c:\php\ext

第三步:去掉565行;extension=php_mysql.dll前面的”;”注释符号

;extension=php_mcrypt.dll将前面的;号去掉

第四步:复制php.ini到Winnt目录下,再复制php5ts.dll和libmysql.dll到WINNT\system32\中

第五步:打开IIS,添加一个php后缀. (指向c:\php\php5isapi.dll) 
isapi筛选器里添加c:\php\php5isapi.dll

第六步:重新启动IIS

然后将 php安装目录/ext/php_mysql.dll  
php_mcrypt.dll 复制到 “c:/windows/system32/” 下

测试

<?php
echo phpinfo();
?>

isapi筛选器加载不了PHP的原因:

要将C盘的PHP目录的权限加上user,可写可修改,就可以加载

还有重要一点,如果按上面配置,打开PHP文件,出现“无法找到该页,您正在搜索的页面可能已经删除、更名或暂时不可用。”的话,这个记得在IIS里面的WEB服务扩展加上PHP的服务扩展。加一个PHP指向c:\php\php5isapi.dll,并设为允许。

黑莓 Bold9000和Nokia N97的一些使用

黑莓 Bold9000

复制:按下aA键之后滑动光标,选中需要的内容–黑莓键–复制。
粘贴:在需要粘贴的地方按黑莓键–粘贴。
彩信翻页:
光标移到页面的最后,按下ALT键再向右滑动下轨迹球就好了;还有种方法不用翻页,就是在看这条彩信前按F键,也就是转发,这样就可以一页显示完整条彩信的内容,不用翻页了。

复制粘贴的进阶版:只使用aA和轨迹球的确定键。
例如要复制一段话,按住aA键选中那段话,再按轨迹球,出现菜单,选择复制;在要粘贴的地方直接先按住aA再按下轨迹球,这样就直接粘过去了,更快速,不需要使用黑莓键。

Nokia N97
关闭虚拟键盘:设置-程序管理-已安装的程序-找到目标应用-选项-套件设置-关闭虚拟键盘

【贪吃蛇—Java程序员写Android游戏】系列 0. 前言几句话

这段时间以来,本博陆续介绍了Android系统的开发和调试环境、模拟器,并详细解析了Android的一个实例SoftKeyboard,这里具体列出如下:

基础已经有了,因此后续准备写名为《贪吃蛇—Java程序员写Android游戏》的系列文章。

之所以选择以《贪吃蛇》为例和切人点,有如下几个原因:

  1. 贪吃蛇是本人在手机上玩到的第一个有趣的游戏,想必对很多朋友来说也是这样,实现简单却又极具可玩性。
  2. 贪吃蛇可以有很多版本,很多变种,可以做得很简单,也可以做得很复杂。可以2D,也可以3D。
  3. 贪吃蛇,最早的版本是蛇吃苹果,让我们想到伊甸园的蛇,吃了智慧之树的苹果,而我们也要像亚当和夏娃那样,不断的吃苹果,获得智慧,充实自己。

闲话少叙,本系列文章初步的计划是,由Android SDK
Sample的贪吃蛇游戏(Snake)为切入点,通过跟J2ME手机游戏和J2SE桌面游戏开发的比较,逐步介绍Android游戏开发的相关知识和流行框架。

本系列要求读者具有Java编程基础,最好学过基础的Java语法。如果进行过J2ME程序的开发就为适合。当然,如果你做过J2SE的开发也不错,不过,这十年Java应用最多的想必是JEE吧。(^-^)

让我们一起做“贪吃蛇”,不停地吸收养分,不断地壮大自己吧!
欢迎大家的批评指正。

偷了世界的程序员

本文译自美国时代(time.com)的《The Men Who Stole the World 》,原作者:Lev
Grossman。相当有传奇色彩,读起来很爽,翻译过来。译得不好,还请大家指正。本中的四个程序员可能并不是那么声名显赫,而且也很不老实,或许算不上成功,不过他们的确改变了世界。而本文有分析了互联网上P2P的那些事,相当的有参考价值


————————正文————————


十年前,有四个年轻人改变了这个世界的运作方式。他们使用的并不是法律或是武器或是金钱,而是使用软件来改变世界。他们当时有着激进和极具破坏性的想法,并把这些想法付诸于代码,在Internet上以免费自由方式发布。这四个人,没有一个完成了大学学业,却奠定了今天我们习惯的数字媒体环境的基础。然后,因为各种原因,他们也迅速地消失在公众视野中。

1999年,美国东北大学的一个叫Shawn
Fanning的一年级新生开发Napster,从此,成为了P2P文件共享和不需要大型机构或零售商就可以获得音乐的先锋和范例。《时代周刊》和《财富》把他放上了封面。那时,他在19岁。

就在同一年,一个挪威的只有十几岁的年轻人 Jon
Lech Johansen,他和另两个今天都不为人知的程序员,写下了一个程序解密了商业的DVD,而他成为了全球盛名的“
DVD Jon.”,那年,他只有15岁。

而在1997年,Justin
Frankel,一个亚利桑那州塞多纳的18岁的黑客,开发了一个费的MP3播放器——WinAmp,其成为了Windows操作系统上装机必备的软
件,并造就了主流数字音乐的革命。在他发布的第18个月内,1500万人下载了这个软件。而三年后,Frankel 开发了
Gnutella,一个P2P的文件共享协议,没有中心结点,不像
Napster,其不可能被关闭。目前有上百万人还在使用它。

2001年,Bram Cohen, 当年 26
岁,开发了一个P2P的文件传输共享协议——
BitTorrent
,其以全新一流的架构全面优化了网络上大文件的共享和传输效率。 BitTorrent
也变成了整个Internet上发布大数据和文件的一个标准。



2000年代的上半段,《时代》采访了这四个程序员。那个时候,看起来他们要以数字化动乱把整个复杂的传统媒体娱乐平台给拆除,而对有版权的电影,音乐和
电视的收费则变得困难和不可能,那些艺术家也将无法从他们作品得到报酬,整个娱乐业包括时代华纳也将被炸为平地。而盗版业则借着这四个程员的软件侵袭了美
国公司。

“毕竟”,我们在2003年报道到:“在整个信息经济中,不可能所有的信息都是免费的”。如果毁灭正在来临,那么,
Fanning, Johansen, Frankel 和 Cohen
将是那“四骑士”(译注:启示录中的四骑士传统上被解释为瘟疫、战争、饥荒和死亡)。

Shawn Fanning(左) 和 Bram Cohen(右)

没有毁灭

毁灭并没有发生。但是整个娱乐业因此而改变,而这些改变的复杂性和逐渐演进超出了我们的期望。这些发生的故事,
海盗王们的事,对于今天数字化世界正在发生的事情有非常高的参考和教育价值。Fanning, Johansen, Frankel 和
Cohen
现在都硅谷运作着自己的小的,合法的软件公司。他们现在没有在做和盗版有干系的事情——当然,如果他们真的没有。

Fanning,四个人中唯一一个没有回复我们的采访请求的人,他较早地退出了毁灭传统唱片业的事业。在2001年,Napster因为不堪众多关于其协助并煽动版权侵权的法律诉论的重压,而不得不关闭。2002年,Fanning
创办了新的服务 Snocap —— 他尝试把文件共享合法化,在和相关的唱片公司合作下,Snocap
赋予消费者对其下载作品给于创作者报酬的权利。

但是,到那个时候,免费自由的文件共享程序像病毒一样的增涨,而用户则热衷于更换他们的音乐硬盘。他们仅在2001年8月一个月内就交换了30亿个
文件。而要从这些文件交易中收到钱是根本不可能的。是的,要和免费竞争是很难的一件事。 Fanning
创造了一个连他自己都搞不定的怪物。

所以,他停止继续尝试Snocap下去。
Fanning 的下一个项目是给游戏玩家的社交网络叫
Rupture,最终,他在2008年时以1500万美金把其卖给了电子艺界Electronic Arts
——这是他的第一次发薪日。他现在又于2008年11月开了一个公司 Path,
其主要提供给iPhone手机进行照片分享的服务。

而Napster呢?今天他还在。这个商标在破产拍卖时被卖了,然后再被卖了,但其再也没有被
重建。现在其被  Best Buy 运营,其是 iTunes 的竞争者,其口号是—— “More
than just a music store.” (不仅仅只是音乐商店)

没有盗版的人


作 为 Gnutella 的作者, Justin
Frankel 是 Fanning 合法的继任者。不像
Fanning,他很早就收获了他的第一桶金。在1999年,当WinAmp大放光芒的时候,AOL买了WinAmp和他的公司——Nullsoft,价
格应该在1亿美金左右。这让 Frankel 在20岁的时候就非常富有。当然,他也成了AOL的员工。

但这并不是很匹配,在Nullsoft,
Frankel的做法是把软件开发到极致,然后免费发布出去。而在
AOL,软件的商业销售威胁并压倒了软件本身。“我致力于的产品,就像这样,我们不愿意金钱的掺入,我们正和其它公司做这笔交易,所以,产品也只能是这样
的结果”,他回忆到,“没有人真正地去关心用户的体验是怎么样的”。

与此同时,Frankel 用他的业余时间开发
Gnutella
。这是一个很有才的软件,不像Napster,其是真正的分布式,没有中心服务器,这样,也没有那个“关闭按钮”让那些律师按。在2000年3月的时
候,Gnutella上线,其发了一个贴子:“看见没?AOL也能给你一些好的东西!”,但是就算是这样,也没有换来AOL对其忠爱,而一大堆互联网公司
在那时试图并入大的媒体公司,在Napster被诉讼的中期,2004年,他离开了AOL。

然后,他开始干了些有趣的事:他离开了他的成功地,他不用
Gnutella,也没有花一毛钱,就算是10年以后也是这样。 LimeWire —— 最流行的 Gnutella 客户端 —— 号称有
5千万用户。“当我开发它的时候,我最初主要是想用其在验证一下是否可行。所以我也不想从其中获益”,他说,“所以,甚至我和它一点关系也没有也说得通,
其就是一个概念”。

Frankel
他最近从旧金山搬到了纽约城,现在全心打理自己的公司 Cockos (别问为什么叫这名),这是一个关于音频产品套件,叫
Reaper。他坚持不懈地改进着它,并且他和他的用户保持着很近的关系,其用户数大约是几万人。“当前的策略我们并不想发展用户数量”,他说,“我们只
是在享受目前的过程,并在做正确的事情”。他并不同意他是这个世界上最危险的geek,而滚石在2004年时对他则是这么认为的。“我不觉得盗版是很危险
的”,他说,“根本上来说,大众的商业模式总是依赖于对所有事情的强控制——尤其是那些有瑕疵的模式。而作为一个软件开发者来说,多少会产生一定程度的盗
版”, Gnutella
对他来说已是远古的事情了。“数字化盗版:它毁了唱片业了吗?没有。唱片业适应了吗?当然,很多人会说得更好。你应该更关注质量,以及更小一些乐队,等等
这类的事”。

“至于音乐流行和排行这么大的市场,这点盗版算什么?” 他边说边笑道,
“我希望就是这样。”

四眼怪兽

在这四骑士中,只有 Bram
Cohen他现在还在致力于其10年前的那个项目。他是
BitTorrent的创始人和首席科学家,而一个令人敬佩的旧金山的公司希望能把Cohen的这个令人瞠目的高效的内容分布式技术变成商业化应用。

这是一个奇特的公司:其合法的业务建立在一种仍然可能被用来进行大规模版权侵权的技术上。即使像
BitTorrent这样被8千万用户安装了的东西,其看上去还是像刚刚开始创业一样。
在BitTorrent上有相对较小的一部分是完全合法的 ——
最近的一个研究表明完全合法的部分占11%。而在这11%中,有更少的一部分产生了BitTorrent的收入。

就像 Fanning 的 Snocap
一样,Cohen
试图把其BitTorrent从大量的盗版领域转移到合法的领域,这样才能挣到钱。2007年是BT发展最震动的一年,BitTorrent成为了20世
纪福克斯、派拉蒙、华纳兄弟 和 米高梅 影业公司的合作伙伴,和他们一起共同形成了 Torrent
Entertainment Network,主要提供电影,电视,电子游戏的购买和零售。

就像 Fanning一样, Cohen
明了要摆脱盗版并不像看上去的那么容易。“所有的和它有关的事都是灾难”,他说到。Torrent Entertainment
Network 于2008年底关闭。回想起来,你能明白这为什么不行。
BitTorrent在用户友好上做得还不够,并且,在其底层也不够有效率。它可以很快地像病毒一样地移动大量的数据。然后,当你要在上面算钱的时候,你
不得不把速度给降下来,然后跟踪并控制其下载流,还和使用一些很扯淡的诸如“数字版权管理(DRM)”之流的技术,其大量地限制了用户那些是可以干的,哪
些是要买的。

“我从这次失败中学到了很多很多的教训”,
Cohen
悔恨地说。他现在的策略是只和那些只需要他的BT中的“快速”和“病毒式分布”的人合作。“与其去和那些内容提供商合作,为他们加上特权,以扩展我们的渠
道,我们还不如直接获取那更大的渠道,那里的人更喜欢更为开放的方式”。

迄今,对些感兴趣的独立电影制片商叫 Four
Eyed Monsters
(四眼怪兽)和 一个叫 Pioneer One
(先驱者一号
)的电视剧集的创作团队。说起来有点沮丧:Cohen正坐在一个消防水带上,一个程序员所梦想的成功的技术却失控了,而大的玩家又不想来玩。

以他的编码天份,Cohen可以很容易的进入一家大型的公司。但那并不是他的风格。“我的确需要一定的自由度”,他说。他现在正在开发一个全新的事
情——一个P2P的实时数据流的系统,而不是分散的文件。这个项止将可能有巨大的潜力,尤其在新闻、体育等事的互联网上的现场直播。当然,他还在维护着
BitTorrent,但他没有花太多的时间在上面。他说:“当我开发它的时候我就知道没错”。

简单之道

那么,在去年,盗版导致了什么?在美国,每个人都认为盗版对内容制造者的影响并没有那么坏。一份去年四月份美国审计署的报告,非常牵强地把盗版和滞销给联系在一起,但其结果尚无定论。

打击盗版在今天扁平化的世界上并不那么成功。无政府主义的世界观加上那些无与伦比的代码,不可能在那些合法的津津计较的商业界里传播。好的代码应该
给用户有不同的选择,用户使用他们也并不一定是对行业有益的。而你真正需要的是向那些合法商业界挑战,挑战他们那些限制用户做用户想做的事的那种独裁
性。(译注:这让我想到了腾讯360还有敏感词)

另外一个重要的原因是唱片业的灾难是不会发生的。Steve Jobs 在
2003年4月28日,那段时间是互联网文件共享井喷的时候,Apple揭开了iTunes Music
Store
的面纱。在那个时候,我们都觉得iTunes不可能成功,就像Snocap
以及他和它类似的项目都以失败告终。这是因为,你怎么可以可能和免费竞争呢?

但是iTunes
确实成功了。Apple无情地强调着简单和有魅力的用户接口,以及有乔布斯对唱片业的那强有力的谈判,造就了一个最新型的专业的服务,其可以让你放心地下载并传输音乐。的确是做到了,尽管其是收费的,而且我们的购买需要和DRM(数字版权管理)扯上关系并限制我们。

于是,我们看到了可以和免费竞争的东西——简单(译注:个人以为可能还需要加上一点时尚)。Napster,
Gnutella 和 BitTorrent
从来没有在用户友好度上到达像Apple那样的境界。从来没有人在网上检查并整理那些文件内容,所以,当那些众多的文件被共享时,我们可以看到,很多文件
加杂时广告,色情,木马,病毒以及其它一些垃圾。当乔布斯为我们提供了那条简单之路,我们接受了。很明显,自由太过头——至少数字媒体是这样的。

这是一个让那些年轻的海盗王们认真学习的教训。就像
Fanning, Frankel 和 Cohen一样, 其实,Jon Lech Johansen
并不能算得上是一个真正的海盗。他没有因为想把好莱坞搞破产而去帮助破解DVD,他这样做是因为他想在他的电脑上看电影。他的电脑安装的是Linux操作
系统,而1999年,在Linux上根本没有可以用来播放DVD的程序,所以,他和他的伙伴们决定自己写一个,所以,他们不得不先把DVD给解密了。

当美国电影协会( Motion Picture
Association of America)发现了DVD被破解的这个事,其向挪威政府控告 Johansen,并拘留了他。
他在奥斯陆(挪威的首都)受审两次,不过两次都被宣告无罪。因为他解密的DVD是他付费购买的。

但Johansen真正的明白消费者对其购买的数字媒体的权利,这就好像一本书一样——我们可以不断的使用这本书,或是把这本书借出去,这是我们的
权利。2005年, Johansen 去了加利福尼亚,在那里,他逆向工程了
FairPlay,这是苹果公司的用来保护其多媒体文件的DRM类软件(译注:这是苹果公司用来加密iPod的工具)。之后,他注意到了苹果公司产品的用
户体验是多么的迷人,所以,他在想,应该把这些东西带给全世界给那些更为无序的非苹果的产品。

“我们看到这世上有很多很多的产品,但其并没有像他们那样良好地运作”,Johansen说,那时他26岁的程序员。“所以,我们应该开发一个系统,其可以让这些设备的整合起来并给消费者他一个相当不错的用户体验”。

所谓的 “我们”,就是 Johansen
自己的公司—— doubleTwist,这个公司于2007年创建。 doubleTwist 软件是免费的,是一种像罗塞塔石
一样的为数字多媒体软件文件开发的软件——它是可以翻译,和谐并组织大约500种不同设备的文件,把他们放在一起并提供一个相当漂亮的接口。其6月份, doubleTwist
摧出 Android App,当时就有超过50万的用户下载了(译注:大家可以Google一下 , 好评如潮)。去年,
doubleTwist 开始了他的政变打出了这样的广告:“The Cure for iPhone Envy. Your iTunes
library on any device. In
seconds.”(嫉妒iPhone的对策。你的iTunes库可以在任何设备上,只需几秒钟。)它这个条幅挂在了苹果在旧金山的旗舰店的外墙上。

Johansen
拒绝承认他和盗版有关系。“至于我被所指责的,真的和我没有什么关系”,他说。“我支持公平使用,意思是你的确是需要合法地获得内容,但你应该有权利使用
任何一款设备或是应用程序来查看那些内容”。 Johansen
像所有的海盗王一样,他总是能写好的代码,而这些好的代码给了人民使用的权力。这才是盗版灾难不会发生的真正原因。盗版永远不希望所有的音乐和电影或是其它的东西成为免费的,他们想要的“free”其实是自由!

————————————正文结束————————————

最后一句话是点睛之笔,作者对这个世界的认识真是相当的透彻。所以,加粗了。我个人理解本文带给我如下的启示:

  1. 年轻就应该豁得出去,就应该有天不怕地不怕的想法,并付诸于行动。
  2. 互联网上的盗版永远不会停止,与其说是盗版,其后面则是自由和无政府主义。
  3. 自由过度并不是那些利益集团所希望的,并可能会让你惹上麻烦,不过这世界总是因此而改变。
  4. 版权限制和免费并不是最好的,而最根本的是尊重用户的自由权以及不断地化繁为简以改善用户的体验。

另,
题外话,最近一段时间都在招人,有一天,一个同事和我说,“现在的这些程序员怎么回事啊?我问他们:‘你心目中的最牛的程序员是谁?’,居然回答不出来,
有人说是Bill Gates,还有人说是马云,气死我了……”。我想想也真是可笑,难道,Dijkstra,Linus,Ken
Thompson,Dennis Ritchie,Richard Steven,Bjarne Stroustrup……
这些人不认识吗?就知道有钱人,哎,这个时代真是个文化缺失的年代!。


转自:http://coolshell.cn/articles/3363.html

用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

在一个优秀的国外个人网站(Darren
Abbey)上发现的,大家可以尝试证明它们。估计后面你会崩溃,呵呵。

或者,用程序证明也不错,欢迎讨论。

用A4折出正方形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

可以折出正方形,后面的就有了基础。。。。

用A4折出等边三角形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

用A4折出正八边形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

用A4折出正六边形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

用A4折出正五边形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

用A4折出正七边形
用A4折出正方形,等边三角形,正八边形,正六边形,正五边形,正七边形

有没有一点崩溃的感觉呢?

J2ME游戏源代码—Eliminator Demo

 

Eliminator是我翻译的《J2ME游戏开发》中的实例,这里给出它的Demo版本,没有什么游戏性,但是演示了基于MIDP2.0的手机游戏开发中,游戏的基本框架。其可执行文件如下:

附件 Eliminator-没有敌人的版本(可执行文件).rar

Eliminator是一个垂直滚动背景的射击游戏,这里是它的Demo,包括了开机的启动界面、主界面、还有游戏运行场景,但是没有加入敌人。相关的界面如下:

J2ME游戏源代码—Eliminator <wbr>DemoDemo” TITLE=”J2ME游戏源代码—Eliminator Demo” />

如果你对这个游戏感兴趣,这里有它的工程文件,使用JBuilderX建立。工程文件如下:

附件 Eliminator-没有敌人的版本(工程文件).rar

如果想仔细阅读本游戏,并制作出自己的游戏,请参考我前文《J2ME游戏开发(J2ME_Game_Development_with_MIDP2中文版)》中的附件,它是本游戏对应的pdf格式的电子书。

这里给出了目前最完善的可执行文件和JBuilderX的工程文件。

Eliminator是我翻译的《J2ME游戏开发》中的实例,这里给出它的Demo2版本,仍然没有什么游戏性,但是有所改进。其可执行文件如下:

附件 Eliminator-精简版本可执行文件.rar

Eliminator是一个垂直滚动背景的射击游戏,如书中第71页所述:”这里使用Sun公司的WTK2.0(J2ME
Wireless Toolkit version
2.0),它在渲染滚动的TiledLayer时有一些问题,Nokia的J2ME开发包对滚动TiledLayer处理得更有效率,最好的选择是使用
Nokia的开发包。在本书创作过程中,据说Sun公司新发布的WTK已经解决了这些问题。”在使用JBuilderX建立工程文件时,如果使用其自带的
WTK(”J2ME Wireless Toolkit 2.0_01″),游戏在模拟器运行就会非常顿,而使用”J2ME Wireless
Toolkit 2.1_01″就没有这种情况,建议读者到http://wireless.java.sun.com/下载。但是请不要使用最新的”J2ME
Wireless Toolkit 2.2″,否则游戏不能正常运行。

Demo2包括了开机的启动界面、主界面、还有游戏运行场景,但是没有加入敌人。相关的界面如下:

 

 

J2ME游戏源代码—Eliminator <wbr>DemoDemo” TITLE=”J2ME游戏源代码—Eliminator Demo” />

J2ME游戏源代码—Eliminator <wbr>DemoDemo” TITLE=”J2ME游戏源代码—Eliminator Demo” />

J2ME游戏源代码—Eliminator <wbr>DemoDemo” TITLE=”J2ME游戏源代码—Eliminator Demo” />

如果你对这个游戏感兴趣,这里有它的工程文件,使用JBuilderX建立。工程文件如下:

附件 Eliminator-精简版本工程文件.rar

如果想仔细阅读本游戏,并制作出自己的游戏,请参考我前文《J2ME游戏开发(J2ME_Game_Development_with_MIDP2中文版)》一文中的附件,它是本游戏对应的pdf格式的电子书。