C#服务端的微信小游戏——多人在线角色扮演(十三)

C#服务端的微信小游戏——多人在线角色扮演(十三)

面向对象,春暖花开
——茂叔

文字渲染的优化

上一篇,我们实现了文字渲染。但是,细心的朋友应该发现,尽管我们可以渲染出文字,但整个过程非常的耗时。
首先,我们需要根据内码去获得文字的区位码,这一过程需要去解析一个非常长的字符串。想想都觉得累。
其次,我们获得了区位码后,需要根据一串字节去逐位画点,也就是说,我们渲染一个汉字实际上画了很多个点,系统消耗非常大。

据实际测试,在一台性能不错的笔记本上,渲染5000个汉字,居然耗时300毫秒以上。作为游戏,这是不可接受的,一秒3帧,还玩个P啊!
因此,我们需要对文字的渲染算法进行优化。

既然实事解析内码去获得区位码非常耗时,那我们可以把内码与区位码之间的关系放进一个Map对象,通过映射关系快速查找到内码所对应的区位码。

对于逐一描点这种累人的活,我们可以提前将所有汉字点都描出来,在运行时只需要根据区位码找到对应的图像,然后截取出来就可以了。

于是,我们对han.js做了一些“小”改动:

var fontImg = wx.createImage()

exports.install = (callback) => {
   
   
  fontImg.src = "images/han.png";
  fontImg.onload = callback
}
GameGlobal.drawText = (ctx, str, sx, sy) => {
   
   
  var cx = 0
  for (var i = 0; i < str.length; i++) {
   
   
    var charCode = str.charCodeAt(i)
    var quwei = CodeMap.get(charCode)
    ctx.drawImage(fontImg, quwei.wei * 12, quwei.qu * 12, 12, 12, sx + cx, sy, 12, 12)
    cx += 14
  }
}

var CodeMap = new Map([[12288, {
   
    "qu": 0, "wei": 0 }], [12289, {
   
    "qu": 0, "wei": 1 }], [12290, {
   
    "qu": 0, "wei": 2 }], [183, {
   
    "qu": 0, "wei": 3 }], [713, {
   
    "qu": 0, "wei": 4 }], [711, {
   
   ……

//代码不齐,太长了,两次让我的浏览器停止响应,所以请到GitHub去下载完整代码吧,我也不想这样

其中,CodeMap是根据那个很长很长的字符串解析出来的,具体解析过程略……重点就是每个内码对应一个区位对象。
han.png是按区位码排列的8000多个汉字点阵图,居然比二进制字库文件还小……
在这里插入图片描述
每行94个字符,一共94行,正好对应区位码。
所以,使用

ctx.drawImage(fontImg, quwei.wei * 12, quwei.qu * 12, 12, 12, sx + cx, sy, 12, 12)

这一句就很方便的把图中对应区位码的图像画到我们需要的地方了。

经过这两个修改,我们渲染5000个汉字的时间只需要不到50毫秒,嗯20帧,凑活玩了。
实际使用中,以我们的界面大小,不会有一帧超过200个汉字的需求,而如果渲染200个汉字,只需要5毫秒,一秒200帧,愉快了……

按钮?窗体?

Windows系列系统中,窗体、按钮、控件什么的,都是从一个基类派生出来的,有完善的消息机制和渲染策略。
我们的小程序中也可以采用这样的策略来组织我们的界面。
具体的思路是,一切需要渲染到设备上的界面元素,从主界面到一个标签、一个按钮、都从一个基本渲染对象派生而来,封装好的对象可以在写码时方便的实例化,从而使界面代码更加有序和高效。
首选,我们在根路径新建一个UIOBJECT,js文件,在里面定义一个基本对象:

function baseUIObj(father, x = 0, y = 0, w = 0, h = 0){
   
   
  this.father = father || undefined
  this.x = x
  this.y = y
  this.width = w
  this.height = h
}

这个对象作为我们所有界面元素的基类。
我们只需要使用:

var newUIObj=new baseUIObj()
console.log(newUIObj.width)//输出:0

var newUIObj2=new baseUIObj(newUIObj,0,0,100,30)
console.log(newUIObj2.width)//输出:100

就可以获得这样一个界面元素,创建时可以输入它的父元素,或者说容器,以及在父元素中位置和尺寸。
然后添加它自己的canvas对象:

  this.canvas = wx.createCanvas()
  this.canvas.width = w
  this.canvas.height = h
  this.gCtx = this.canvas.getContext('2d')

在定义一个渲染的方法:

baseUIObj.prototype.redraw = function(caller) {
   
   
  if (caller.isTouch)
    caller.gCtx.fillStyle = caller.colorOnTouch
  else
    caller.gCtx.fillStyle = caller.color

  caller.gCtx.fillRect(0, 0, caller.width, caller.height)

  if (caller.background)
    caller.gCtx.drawImage(caller.background, 0, 0, caller.width, caller.height)
  if (caller.backgroundTouch&&caller.isTouch)
    caller.gCtx.drawImage(caller.backgroundTouch, 0, 0, caller.width, caller.height)

  if (caller.borderWidth > 0) {
   
   
    caller.gCtx.strokeStyle = '#fff'
    caller.gCtx.lineWidth = caller.borderWidth
    caller.gCtx.strokeRect(caller.borderWidth / 2, caller.borderWidth / 2, caller.width - caller.borderWidth, caller.height - caller.borderWidth)
  }
}

以后直接调用这个方法就可以在它的canvas上渲染它所代表的界面了。
当然,还有很多其他的成员和方法,都是管理界面元素所需要的,详细自己读代码吧,最后的代码是这样的:

function baseUIObj(father, x = 0, y = 0, w = 0, h = 0) {
   
   
  this.text = "baseUIObj"
  this.name = "base"
  this.me = this
  this.children = []
  this.father = father || undefined
  if (this.father) {
   
   
    this.father.children.push(this)
  }
  this.x = x
  this
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值