查看原文
其他

写给设计师的OF编程指南7 - 程序流程控制-条件语句 (下)

2016-07-10 Wenzy InsLab

在窗口显示文字

这节的最终目标是完成一个文字冒险游戏,所以要先了解如何在程序中显示文字以及图片。之前输出的文字都在控制台上。但当我们导出程序的时,实际上是看不到控制台的。需要想办法把文字显示在窗口中。

显示文字的简便方式

如果你对字体的样式没有要求,只需用一个命令就能实现显示效果

代码示例(7-1):

void ofApp::setup(){    ofSetWindowShape(400,400); } void ofApp::draw(){    ofBackground(0);    ofDrawBitmapString("Font is here", 50, 50); }


调用格式:

ofDrawBitmapString(str,x,y);
  • ofDrawBitmapString 函数的作用是在窗口绘制字体。参数 str 写的就是文字内容,一般使用字符串类型,所以需要打上双括号以表明变量类型。除此之外也可以写整形,浮点型等数据。

  • 参数 x , y 代表的是字体显示的横纵坐标。这个坐标会决定字符左下角顶点的位置。

直接使用 ofDrawBitmapString 命令,字体的大小都是固定的。如果你希望在窗口显示一些变量数据,而不是控制台,就可以采用这种省事的方式。但此命令只能用来显示英文字体,显示中文需要使用其他方式。

显示中文字

OF 中不像 P5 ,可以直接使用中文。它必须结合插件 ofxTrueTypeFontUC 才能正确显示。在正式写代码前,你必须做好准备工作。

首先在官网 addons 中寻找这个插件()


进入到 github 页面后,选 Clone or download 下面的 download ZIP 即可。

下载解压缩后,它的文件名会是 ofxTrueTypeFontUC-master。请记得将后面的 -master 去掉。并将整个文件夹剪切到 OF 自身的 addons 文件夹中。

当你完成上面的步骤,在通过 project generator 创建文件时,就会在addons 处找到 ofxTrueTypeFontUC。当你以后做的项目需要用到中文字体时,请记得点选它,最后才点 Generator 按钮来生成工程文件。


现在只是安装好插件,这个工程文件上还没有放字体文件。你可以从电脑的字体库中寻找一个支持中文显示的字体,格式建议为主流的 otf,ttf 或 ttc。并将它复制到工程文件的 data 文件夹中。

若是找不到具体位置,可以在 xcode 中右键 src 的文件夹,选 Show in Finder 就会自动弹出


在 bin 里面就能找到 data 文件夹,将 otf 字体文件复制进去。


接着你就可以正式敲代码了。

代码示例(7-2):

—- ofApp.h 内

#include "ofMain.h" #include "ofxTrueTypeFontUC.h" class ofApp : public ofBaseApp{    public:        void setup();        void update();        void draw();        ...    ofxTrueTypeFontUC font; };

—- ofApp.cpp 内

void ofApp::setup(){    ofSetWindowShape(700,700);    font.loadFont("font.otf",80); } void ofApp::draw(){    ofBackground(0);    font.drawString("中文字体", mouseX,mouseY); }

代码说明:

  • ofApp.h 内,有一句前面没提到的新语法。就是 #include “ofxTrueTypeFontUC.h” 它代表在程序中调用插件。前面的工作只是将插件包含到工程文件中,并没有真正在程序中使用。以后如果希望加入其他插件,导入的方法都是类似的,在生成时先将插件加到工程文件上。再在 ofApp.h 内,写上 #include”插件名.h” 进行调用

  • 加载插件后,你就能使用 ofxTrueTypeFontUC 这个新的数据类型了。

  • ofxTrueTypeFontUC font; 表示声明的是 ofxTrueTypeFontUC 类型,它就像 int ,float。只是它储存的不是数字,而是字体。后面的 font 只是代表一个变量名,它是可以任意取的。

  • 创建了 ofxTrueTypeFontUC 类型,就相当于有了一个容器。我们需要告诉程序,这个容器里面存放什么。setup 中的 font.loadFont(),就代表往 font 中载入一个名为”font.otf”的字体。这是思源黑体,为了便于输入,就将字体文件改成了“font.otf”。第二个参数则会控制字体的显示大小。

  • 为什么会有”.loadFont” ? 似乎和前面提到的函数调用方式都不一样。这里的点代表 loadFont 这个函数与 font 这个变量之间是父子关系,这个函数是属于 font 的。通过 font.loadFont 去调用,就只会影响 font 这个变量。现在可以无需深究,先熟悉这种调用方式

  • draw 中的 font.drawString() 为真正的执行部分,第一个参数填写字符显示的内容,第二第三个参数填写字符的横纵坐标。这个坐标决定字符的左下角所处的位置。

当你尝试往 OF 程序中输入中文字符时,会出现这个提示。

这是因为字符中包含了中文,这时只要点 Convert to UTF8 就能正常输入。

改变字体颜色

可以将字体看作图形,它就像 ofDrawCircle 或 ofRectangle ,通过 ofSetColor 就能控制字体的颜色。

代码示例(7-3):

—- ofApp.h 内

   #include "ofMain.h"    #include "ofxTrueTypeFontUC.h"    class ofApp : public ofBaseApp{        public:            void setup();            void update();            void draw();            ...        ofxTrueTypeFontUC font;    };

—- ofApp.cpp 内

void ofApp::setup(){    ofSetWindowShape(700,700);    font.loadFont("font.otf",80); } void ofApp::draw(){    ofBackground(255);    ofSetColor(125);    font.drawString("中文字体", 150,300);    ofSetColor(255,155,100);    font.drawString("English", 150,500); }


在窗口显示图片

与创建字体类似,也有专门为图片打造的数据类型。在程序中载入图片之前,我们需要先把图片文件放在对应的文件夹中。

图片文件与字体文件一样,都放在 data 文件夹中

准备完毕后,就可以开始写代码

代码示例(7-4):

—-ofApp.h内

ofImage pic;

—-ofApp.cpp内

void ofApp::setup(){    ofSetWindowShape(700,700);    pic.load("1.jpg"); } void ofApp::draw(){    ofBackground(0);    pic.setAnchorPercent(0.5,0.5);    pic.draw(ofGetWidth()/2,ofGetHeight()/2,400,400); }


代码说明:

  • ofImage 是用于存放图片的数据类型。

  • load 函数的作用是载入图片“1.jpg”。

  • .setAnchorPercent(0.5,0.5); 用于设置图片坐标的相对位置。若不加。图片的绘制方式则与矩形默认的绘制方式一致,坐标为左上角的顶点。开启此命令后,坐标则会居于图片中心。

  • .draw 的作用是显示图片。它有多种参数输入方式。可以添 3 个参数或是 5 个参数。当参数数量为 3 时,第一个参数填写 ofImage 变量,第二,第三参数决定图片的横纵坐标。此时源图片的尺寸有多大,绘制到窗口中就有多大。当参数数量为 5 时,前 3 个参数与参数数量为 3 时完全一致,但后两个参数可以决定图片的长宽。

设置图片颜色

代码示例(7-5):

—-ofApp.h内

ofImage pic;

—-ofApp.cpp内

void ofApp::setup(){    ofSetWindowShape(700,700);    ofSetBackgroundAuto(false);    pic.load("dog.png"); } void ofApp::draw(){    pic.setAnchorPercent(0.5,0.5);    ofSetColor(mouseX/(float)width * 255,255,255);    pic.draw(mouseX,mouseY,400,400); }


代码说明:

  • 这里载入的图片格式是带有透明背景的 png。因此图片不会存在明显的矩形边框。

  • ofSetColor 可以设置图片的颜色。它决定的是图片每个通道的“放光”比例。假如图片某个像素的色值为(r,g,b,a),当使用 ofSetColor(A,B,C,D) 后,新像素的色彩就变为 (r (A/255),g (B/255),b (C/255),a (D/255))。ofSetColor 对于图片的作用,有点像一个滤镜,会对图片中的所有像素都进行同样的操作。当你不写 ofSetColor 时,程序默认执行 ofSetColor(255)。也就是百分百地还原图片的色彩。

  • 示例中通过 mouseX 来改变 ofSetColor 的第一个参数。就可以使红通道的放出量从 0% 变化到 100% 。当鼠标移到左方,红色光减少,绿蓝光就会显现。所以可以看到,doggy 的头像会呈现蓝绿色。

综合运用:文字冒险游戏

希望在开发游戏前,前面的基础知识都已经巩固好了。现在你已经具备制作一个文字冒险游戏的所有条件了。

文字冒险游戏是什么?前不久非常流行的 lifeLine 就能归到此列。如果你是个掌机控,你肯定也听说过逆转裁判系列,它们都是文字冒险游戏中的优秀作品。


if 语句提供了建造程序骨架的可能性,现在你需要寻找“血肉”,将它们依附到骨架之上。如果你希望自己构建的世界更有代入感,图片素材是不可或缺的。请在编码前准备好相关的素材。

即使你既不会画画,也不懂设计,不代表就无法做游戏。不仅是代码,网上其实也有不少游戏美术资源是开源的,并且允许运用到个人作品或者商业作品中。例如 。以下程序用到的图片素材都来源于此。

  • 美术资源链接:

(终极Boss)
(森林背景)
(宝藏)
(骷髅)
(药瓶)

  • 字体:思源黑体,Adobe与谷歌推出开源字体。

我们先来看看实例的最终效果:


看上去已经比较唬人了,但以下代码都没有超出前面的知识。

代码示例(7-6):

ofxTrueTypeFontUC font; int count; ofImage background,bottle,boss,bone,gold; void ofApp::setup(){    ofSetWindowShape(700,400);    font.loadFont("font.otf",15);    bottle.load("bottle.png");    background.load("background.png");    boss.load("boss.png");    bone.load("bone.png");    gold.load("gold.png");    count = 0; } void ofApp::draw(){    ofBackground(0);    // 绘制背景    ofSetColor(255);    background.draw(0,0,ofGetWidth(),ofGetHeight());    // 绘制文字后的半透明背景    ofSetColor(0,150);    ofDrawRectangle(0, 250, 700, 80);    ofDrawRectangle(0, 350, 700, 40);    if(count == 0){        ofSetColor(255);        font.drawString("你為了尋找傳說中的寶藏,进入了黑暗森林。", 30,280);        font.drawString("突然,你和你的小伙伴在路上看见一个神秘药瓶,你打算怎么做。", 30,310);        ofSetColor(150,150,255);        font.drawString("1.踢飞它", 30, 380);        font.drawString("2.自己喝了", 180, 380);        font.drawString("3.怂恿小伙伴喝了", 330, 380);        ofSetColor(255);        bottle.setAnchorPercent(0.5,0.5);        bottle.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    }    if(count == 1){        ofSetColor(255);        font.drawString("水壶翻倒了,突然一股浓烟冒出。", 30,280);        font.drawString("终极 Boss 直接出现到面前 …(⊙_⊙;),你准备", 30,310);        ofSetColor(150,150,255);        font.drawString("1.情况不妙,闪", 30, 380);        font.drawString("2.我是勇者我进攻!", 240, 380);        font.drawString("3.陪它聊聊天", 500, 380);        ofSetColor(255);        boss.setAnchorPercent(0.5,0.5);        boss.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    }    if(count == 2){        ofSetColor(255);        font.drawString("突然间,你拥有了超能力。", 30,280);        font.drawString("感应到宝藏的呼唤,来到了终极 Boss 的面前,你准备", 30,310);        ofSetColor(150,150,255);        font.drawString("1.不啰嗦,直接进攻", 30, 380);        font.drawString("2.陪它聊聊天", 250, 380);        ofSetColor(255);        boss.setAnchorPercent(0.5,0.5);        boss.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    }    if(count == 3){        ofSetColor(255);        font.drawString("小伙伴痛苦地跪倒在地上。", 30,280);        font.drawString("化身成终极 Boss 站到你的面前 …(⊙_⊙;),你准备", 30,310);        ofSetColor(150,150,255);        font.drawString("1.情况不妙,闪", 30, 380);        font.drawString("2.我是勇者我进攻!", 240, 380);        font.drawString("3.陪它聊聊天", 500, 380);        ofSetColor(255);        boss.setAnchorPercent(0.5,0.5);        boss.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    }    if(count == 4){        ofSetColor(255,0,0);        background.draw(0,0,ofGetWidth(),ofGetHeight());        ofSetColor(255);        font.drawString("你的速度太慢了,Boss 向你扔了一个火球", 30,280);        font.drawString("由于实力的差距,你无法抵挡.....", 30,310);        ofSetColor(150,150,255);        ofSetColor(150,150,255);        font.drawString("游戏结束", 30, 380);        ofSetColor(255);        bone.setAnchorPercent(0.5,0.5);        bone.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    }    if(count == 5){        ofSetColor(255);        font.drawString("Boss 被你的气势打倒了", 30,280);        font.drawString("宝藏出现", 30,310);        ofSetColor(150,150,255);        font.drawString("你和小伙伴带着宝藏逃离黑暗森林,通关成功!", 30, 380);        ofSetColor(255);        gold.setAnchorPercent(0.5,0.5);        gold.draw(ofGetWidth()/2,ofGetHeight()/3,170,150);    }    if(count == 6){        ofSetColor(255,0,0);        background.draw(0,0,ofGetWidth(),ofGetHeight());        ofSetColor(255);        font.drawString("Boss 拒绝你的请求,并向你扔了个火球", 30,280);        font.drawString("由于实力的差距,你无法抵挡.....", 30,310);        ofSetColor(150,150,255);        font.drawString("游戏结束", 30, 380);        ofSetColor(255);        bone.setAnchorPercent(0.5,0.5);        bone.draw(ofGetWidth()/2,ofGetHeight()/3,100,100);    } } void ofApp::keyPressed(int key){    if(count == 0){        if(key == '1'){            count = 1;        }        if(key == '2'){            count = 2;        }        if(key == '3'){            count = 3;        }    }else if(count == 1 || count == 3){        if(key == '1'){            count = 4;        }        if(key == '2'){            count = 5;        }        if(key == '3'){            count = 6;        }    }else if(count == 2){        if(key == '1'){            count = 5;        }        if(key == '2'){            count = 6;        }    } }

代码浅析:

  • 为便于展示流程,示例中只设计了两次选择机会

  • 示例只展示基本用法,留待你去扩展补充

  • 其中的 count 变量代表一个计数器。通过它来标记不同的场景。当触发不同的按键,就能在场景间进行跳转,draw 函数也会因应 count 的变化,绘制出对应的场景。

思考题

  • 能否用 string 简化代码?

  • 尝试设计更复杂的分支结构。甚至给角色加上生命力,攻击力,可与终极 Boss 进行决战。

  • 给场景配上战斗动画


导出作品

当你完成一个类似的游戏后,肯定想迫不及待地希望分享给其他人。从 OF 中导出程序十分简单。你甚至无需专门地导出,只要你的程序有被执行(run)过。

你可以在 xcode 中右键 src 的文件夹,选 Show in Finder

它会自动弹出工程文件夹。在里面找到 bin 文件夹。


bin 文件夹中,装载的就是整个可执行程序。之前编译过的文件,默认都是放在这里。此文件可以拷贝给其他人,它就像普通程序一样,只要双击带有 OF 图标的程序就能运行。其他用户无需安装 OF 也能正常使用。

End

希望这节能激起你的创作欲望,代码世界中会有更多精彩的事物留待你去挖掘~


(点击阅读原文可获取示例 7-6 源码)

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存