首页 > 其他分享 >Game = Rust + WebAssembly + 浏览器

Game = Rust + WebAssembly + 浏览器

时间:2023-12-07 21:06:25浏览次数:36  
标签:WebAssembly 游戏 Color self pub Game Coord screen Rust

努力成为一个情绪价值的提供者

大家好,我是「柒八九」。一个「专注于前端开发技术/RustAI应用知识分享」Coder

前言

在上一篇Rust 编译为 WebAssembly 在前端项目中使用我们通过一个简单的Hello WorldDemo,讲述了如何将 Rust 编译为 WebAssembly,并在前端项目中使用。

虽然,是一个Demo;但是,我们由小见大,以点见面,分别描述了

  • Rust 如何编译为WebAssembly
  • WebAssembly如何内嵌到JS环境中
  • WebAssembly如何与JS进行交互
  • Rust如何能被 JS 调用的原理分析
  • web-sys充当wasm-bindgen的前端

在写完上篇文章中,总觉得如果只是一个Demo的话,有点意犹未尽。我们想要在实际开发中使用WebAssembly,总不能通过Rust唤起类似alert等前端唾手可得的功能。这就有点「脱裤子放屁」多此一举了。我们不能为了用而用。WebAssembly在前端项目中,更多扮演的是「功能加速」的角色。也就是我们用了它是为了降本增效的。(不是降本增笑哈)。

并且,在之前的文章中,还有很多开发上的不足,比方说

  • 缺少代码热更新
  • 本地 dev 服务器(上一篇中,我们特意用了Webpack搭建了一个本地服务器)
  • 操作浏览器的DOM元素
  • ...

所以,我们今天用一个功能更加丰富的例子(贪吃蛇游戏),来逐一解决上面的问题,并让大家真正的理解RustJS是如何更好的合作的。

我们的本文的内容,不是要写一个功能完备的贪吃蛇游戏,而是以这个例子来更加完善我们对Rust/WebAssembly/JS之间的数据交互的理解。「重在过程」,当然结果也很可爱,我们会写一个简单版本的贪吃蛇小游戏。

本文的一些基础内容,不再做出过多解释,也就是说大家需要有一定的Rust的基础和如何在浏览器中运行Rust有一定的了解。(可以Rust 编译为 WebAssembly 在前端项目中使用

好了,天不早了,干点正事哇。

Game = Rust + WebAssembly + 浏览器_Rust

我们能所学到的知识点

  1. 前置知识点
  2. 项目初始化
  3. Rust 内部实现&原理
  4. 优化开发
  5. 效果展示


1. 前置知识点

「前置知识点」,只是做一个概念的介绍,不会做深度解释。因为,这些概念在下面文章中会有出现,为了让行文更加的顺畅,所以将本该在文内的概念解释放到前面来。「如果大家对这些概念熟悉,可以直接忽略」

同时,由于阅读我文章的群体有很多,所以有些知识点可能「我视之若珍宝,尔视只如草芥,弃之如敝履」。以下知识点,请「酌情使用」

WebAssembly 是什么

这个问题,在之前也有过解释,参看浏览器第四种语言-WebAssembly

如何在 JS 中调用 WebAssembly

上文中多次提过,参看Rust 编译为 WebAssembly 在前端项目中使用

requestAnimationFrame

requestAnimationFrame 是为了「优化动画性能」而设计的。它会在浏览器下一次重绘之前调用注册的回调函数,以确保动画更新发生在浏览器执行下一帧之前。这样可以避免在浏览器不活跃或隐藏的标签页中执行不必要的动画更新,节省系统资源。从而利用浏览器的优化机制,提供更流畅的动画效果。

requestAnimationFrame 的回调函数会传递一个时间戳参数,表示动画开始执行的时间。我们可以利用这个参数来计算动画的进度,从而实现更复杂的动画效果。

使用方法

function animate(timestamp) {
  // 在这里执行动画更新

  // 通过递归调用 requestAnimationFrame 来实现持续的动画
  requestAnimationFrame(animate);
}

// 启动动画
requestAnimationFrame(animate);

示例

function animate(timestamp) {
  // 在这里执行动画更新
  // 例如:移动一个元素
  const element = document.getElementById("animatedElement");
  const speed = 0.1; // 移动速度
  const timePassed = timestamp - startTime;

  element.style.left = timePassed * speed + "px";

  // 通过递归调用 requestAnimationFrame 来实现持续的动画
  requestAnimationFrame(animate);
}

// 获取动画开始的时间戳
const startTime = performance.now();

// 启动动画
requestAnimationFrame(animate);

尽管 requestAnimationFrame 会在下一帧前执行回调函数,但仍然可能以较高的频率调用。如果需要控制动画的帧率,可以使用其他手段来进行节流。

Clamped 类型处理图像相关

Rust 中,使用 wasm-bindgen 包的 Clamped 类型通常与 WebAssemblyJavaScript 的互操作性有关。特别是在处理图像数据时,ClampedRust 代码中起到了重要的作用。

用途

  • 「Clamped Array(钳制数组)」:在 JavaScript 中,Clamped 通常与 Uint8ClampedArray 相关联,这是一个处理「图像数据时常用的数组类型」Uint8ClampedArray 用于「存储图像的像素数据」,其中每个像素的值被“钳制”在 0 到 255 的范围内,这正是标准 RGBA 颜色值的范围。
  • 「图像处理」:在使用 WebAssembly 处理 Canvas 或者其他图像数据时,Clamped 类型确保了像素值不会超出有效范围。这对于图像渲染非常重要,因为它保证了颜色数据的正确性和一致性。

工作原理

  • 当我们在 Rust 中处理图像数据并准备将其传递给 JavaScript 或者 Web API(例如 CanvasAPI)时,我们可能会使用一组像素数据。这些数据通常是一个字节数组,其中包含了图像的红色、绿色、蓝色和透明度(RGBA)信息。
  • 使用 Clamped 包装器(例如 Clamped(&some_array))时,我们告诉 wasm-bindgen 应该将这个数组视为一个 Uint8ClampedArray,并相应地处理它。这意味着当这个数组传递到 JavaScript 环境时,任何超出 0-255 范围的值都会被自动调整(钳制)到这个范围内。
  • 在我们提供的代码示例中,Clamped 被用来确保在创建 ImageData 对象时,传递给它的像素数组符合 Web 标准,并能被正确地渲染到 Canvas 上。

use wasm_bindgen::Clamped; 的用途是为了在 RustWebAssembly 中处理和传递图像数据时保证数据的正确性和一致性,特别是当这些数据需要与 JavaScriptUint8ClampedArray 类型互操作时。


2. 项目初始化

首先,我们来创建一个Rust WebAssembly项目

cargo new game --lib

这将创建一个包含基本项目结构的文件夹,其中包括一个 Cargo.toml 文件和一个 src 文件夹。

+-- Cargo.toml
+-- src
    +-- lib.rs

创建前端目录结构

在此项目的根目录下手动新增三个文件:

  1. index.html (项目的入口文件)
  2. index.js (逻辑的主入口)
  3. style.css

index.html

在里面其实没啥操作可言,就是将index.jsstyle.css按照资源类别引入。

然后,如果大家在用VSCode编辑器的话,可以使用Emmet命令,一键自动生成Html基础文档。

具体操作步骤简单如喝水,在*.html的空文件中,输入!然后就会出现Emmet的命令,然后选中对于的命令,按下Tab键,就可以自动生成Html文件了。

然后,在其内部引入我们定义的style.cssindex.js。(下面的内容只展示了核心代码)

其中有一点,简单的解释一下,在处理index.js时,我们将其放置到body靠后的位置,这样做的目的是能够在页面充分渲染好时,才会进行对于事件的注册和触发。

<!DOCTYPE html>
<html lang="en-US">
  <head>
    <title>Game</title>
    <!-- 引入样式信息 -->
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <!-- 注意这块,会将rust渲染的内容放置到canvas元素下 -->
    <canvas id="canvas"></canvas>
    <!-- 承接业务逻辑-->
    <script type="module" src="index.js"></script>
  </body>
</html>

style.css

在这个文件中,定义一下元素的样式信息。这就很简单了。我们就不过多解释了哈。

body {
  text-align: center;
  font-size: 18px;
  margin: 0;
}

#canvas {
  width: 320px;
  height: 320px;
  image-rendering: pixelated;
}

@media (min-width: 600px) {
  #canvas {
    width: 500px;
    height: 500px;
  }
}

有一点简单说一下:在#canvas下有一个image-rendering: pixelated

pixelated是一种特殊的图像渲染技术,它将图像呈现为「像素化」的样式,使图像看起来像是由像素点组成的。这种效果通常用于创造复古或像素风格的视觉效果。


index.js

index.js作为逻辑入口,它算是一个粘合剂,将WebAssemblyJS融合到一起,并且能够实现逻辑互通。

玩过贪吃蛇的同学都知道,小

标签:WebAssembly,游戏,Color,self,pub,Game,Coord,screen,Rust
From: https://blog.51cto.com/front789/8727576

相关文章

  • rust webassembly 优化参考资料
    这几天在学习webassembly,尤其是cerboslite对于webassembly的支持,所以简单整理一些chatgpt给的一些建议WebAssembly(Wasm)的优化涉及多个方面,包括编译器优化、代码结构调整、资源管理和压缩等。以下是一些通用的WebAssembly优化策略:编译器优化:使用适当的编译器标志启用优......
  • cerbos lite webassembly 处理简单说明
    上次简单说明了下cerboslite对于webassemblypolicy集成的说明,通过查看liteclient简单说明下参考处理ci/cd集成处理这个也比较符合官方hub的ci/cd机制,核心是通过git的repo管理,集成ci/cd构建webassembly文件,webassembly核心exporter的方法主要是图片右下方的,之后......
  • 一些基于webassembly 的serverless 框架
    基于webassembly的一些serverless框架是比较多的,以下是以下参考,基本上基于rust开发的比较多,当然也有基于golang的参考资料https://wasmcloud.com/https://scale.sh/https://github.com/loopholelabs/scalehttps://github.com/wasmCloud/wasmCloudhttps://github.com/vmware-lab......
  • Spin 基于rust 开发的开源运行基于webassembly serverless 工具
    spin是基于rust开发的,可以用来开发以及运行基于webassemblyserverless服务的工具包含的特性提供了周边扩展 默认wasm只提供了基本类型的支持,wasm提供了不少扩展可以方便的支持不同语言的调用(比如网络,数据库访问)提供了快速应该开发的cli提供了服务部署的能力 包含了本地测试......
  • wasmer 基于webassembly 的平台
    wasmer基于webassembly的平台,目前包含了runtime,registry,edge等组件说明wasmer属于一个插件化的设计,目前支持wasix,wasi以及Emscripten,同时还提供了不少语言sdk方便代码嵌入同时wasmer也提供了就很不错的性能,很值得学习试用下参考资料https://docs.wasmer.io/https://docs.wa......
  • 10. 从零用Rust编写正反向代理, HTTP内网穿透支持修改头信息
    wmproxywmproxy是由Rust编写,已实现http/https代理,socks5代理,反向代理,静态文件服务器,内网穿透,配置热更新等,后续将实现websocket代理等,同时会将实现过程分享出来,感兴趣的可以一起造个轮子法项目++wmproxy++gite:https://gitee.com/tickbh/wmproxygithub:https://github.com/tic......
  • games101-2 透视深度插值矫正与抗锯齿分析
    透视深度插值矫正与抗锯齿分析深度插值的差错原因透视深度插值公式推导games101中的错误msaa与ssaa简要定义games101中ssaa的实现games101中msaa的实现深度插值的差错原因当投影的图形与投影的平面不平行时,这时进行透视投影,从上图中可以看出,投影平面上的线段时均匀......
  • ICPC2022Hangzhou C No Bug No Game 题解
    LinkICPC2022HangzhouCNoBugNoGameQuestion给定\(n\)个物品和上限\(k\),要求最大化分数,物品的选择顺序可以任意第\(i\)个物品一行\(p_i\)代表个数,后面\(p_i\)个\(w_j\)代表容量,定义\(sum=\sum\limits_{j=1}^{i-1}\),对于第\(i\)个物品\(sum+p_i\lek\)......
  • rust使用动态连接库实现两个数的求和
    1.1创建库项目cargonew--libplugincdplugin1.2编写加法功能函数vimsrc/lib.rs#[no_mangle]pubexternfnadd(left:usize,right:usize)->usize{left+right}#[cfg(test)]modtests{usesuper::*;#[test]fnit_works(){......
  • A. Flipping Game
    A.FlippingGame本质上是让我们找出一段区间内\(0\)的个数大于\(1\)的个数的最多的区间,且必须进行一次操作,所以可以考虑区间\(dp\),或者最小子序列和1最小子序列和\[\begin{aligned}dp_i是以a_i结尾的最小子序列和\\dp_i=\min(dp_{i-1}+a[i],a[i])\end{aligned}\]#inc......