圆梦:借助云开发 CloudBase实现你的游戏开发梦想
最近我发现AI产品在不断涌现新动向,尤其是一些技术巨头推出的创新产品。例如,今天我们要探讨的是腾讯云开发的云开发 CloudBase,如果你之前没有听说过这个名字,那可能还记得腾讯云推出的另一个产品——微搭。没错,CloudBase 就是那个将AI能力融入其中的微搭平台,它能帮助用户更高效、更便捷地完成网站搭建,特别是对于不熟悉开发的用户来说,极大降低了技术门槛。
在上一章节中,我对云开发的AI能力做了初步了解,其中提到了通过一句话生成网页的功能,虽然当时我只是略过了一笔,但今天我们不妨深入探讨一下,看看它的可视化开发AI能力到底有多强大!
今天我们要讨论的主题是开发一款枪战游戏——灵感来源于经典的《穿越火线》。而为什么我选择在今天用腾讯云开发平台 CloudBase 来实现这个梦想呢?因为,CloudBase 正是以降低开发门槛、提高开发效率为宣传亮点的,它所提供的多种工具和AI能力能够帮助开发者快速实现项目,甚至是一些不太熟悉编程的初学者也能轻松上手。因此,今天我决定借助这个平台,完成我大学刚毕业时曾经埋藏在心底的一个梦想:开发一款属于自己的游戏。
当然,时间有限,项目规模也不能太大,所以我们决定暂时不考虑3D开发——毕竟我们主要是针对前端PC页面进行开发,这样一来,游戏的表现形式将围绕2D展开。虽然不走3D路线,但我们依然会力求在2D画面中实现充实的游戏体验,加入一些有趣的互动元素和创新的玩法。
那么,话不多说,游戏开发之旅正式开始!
可视化云开发
前期准备
我们不绕弯子,直接进入主题。如果你还没有开通腾讯云的相关服务,只需前往腾讯云控制台,搜索“云开发”并按照提示完成开通即可,整个过程简单直观,非常容易上手。由于此部分内容较为基础,不再在文章中详细展开讲解。如图所示:
在完成必要的环境准备工作后,我们可以直接点击进入相关页面,开始后续操作。如图所示:
进入环境后,我们直接选择“可视化开发”选项,开始进行AI驱动的网站搭建。
好的,所有前期的准备工作已经顺利完成,接下来我们正式进入主题——开始搭建枪战游戏。需要注意的是,在搭建过程中,我们将会遇到许多问题和挑战,涉及到图片滑动、准星瞄准、页面优化等一系列功能的实现与修复。这些细节问题虽然重要,但如果逐一列举和详细讨论,文章的内容会显得过于冗长和繁琐。
因此,本篇文章的核心目的是教授你如何运用搭建技巧来解决这些问题,并帮助你高效地完成游戏的搭建工作,而不是专注于每一个具体的小问题。
开始搭建游戏
默认情况下,云开发平台中包含了多个智能体助手,用户可以根据实际需求通过@标记来调用对应的助手功能。具体操作方法请参见下图所示:
我们将直接使用默认提供的AI生成组件,以便快速测试其效果和性能表现。如图所示:
如果你对当前页面的效果不完全满意,可以随时继续提问,让AI助手帮助你进行页面美化和调整。然而,值得注意的是,虽然AI在优化和设计方面具有很大的帮助,但它并不能完全替代你的工作。实际上,它最多能够替代约70%的工作量,已经是相当不错的表现了。
请记住,AI在生成内容时是从头开始编写代码的,而不是逐行接受和更新,因此每次生成的结果可能会有所不同。看到一个接近理想的页面时,你可以直接创建应用并进行后续的优化和调整,确保最终效果符合你的需求。具体操作过程如下图所示:
素材库
图片素材
当你在创建网站的过程中,通常需要使用大量的图片、图表以及其他各种素材资源,在开始网站开发之前,你可以将这些素材提前上传到平台或者服务器中,以便后续更高效地进行页面设计和内容整合。下图展示了这一操作的具体步骤或示例:
不过,目前在搭建网站时,你仍然无法直接选择这些图片。因此,你的主要任务是复制这里的图片链接。当然,如果你有自己的图床,那就更好了,因为这样你不仅可以直接使用自己的图片资源,还可以随时更新图片内容。需要注意的是,这里提供的素材库无法进行图片的更新操作,只能进行删除或重新上传。因此,这一点可能会成为你操作过程中的一个卡点。
音乐/视频素材
目前,云开发素材库虽然暂时不支持添加音乐或视频文件,但如果你有这类需求,完全可以通过其他方式解决。具体来说,你可以选择使用自己的服务器,将文件通过服务器暴露出来。这并不复杂,我已经为你提前做好了相关配置。你所需要做的就是在自己的服务器上安装并配置一个 Nginx 服务,并为素材文件准备一个专门的文件夹。
这么一来,你就可以通过 Nginx 实现文件的访问和管理,轻松地为云开发素材库提供音频或视频文件了。
第一步:安装nginx
这里我使用的是腾讯云轻量服务器自带的宝塔面板,您只需通过一键安装功能即可快速完成安装,安装过程十分简便,具体步骤如图所示:
第二步:准备素材文件夹
我们只需直接创建一个空文件夹,作为存放素材文件的目录,方便后续进行素材管理和操作。如图所示:
第三步:配置Nginx反代
最后一步,我们直接配置一下nginx的代理,注意这里需要解决跨域问题,不然腾讯云无法直接访问我们的服务器。配置如下:
# 静态文件服务(图片、音频等)
location /media/ {
alias /data/disk/media/; # 媒体文件存放的目录
try_files $uri $uri/ =404; # 文件不存在时返回404
# CORS 配置
add_header Access-Control-Allow-Origin *; # 允许所有域名访问
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; # 允许的 HTTP 方法
add_header Access-Control-Allow-Headers 'Origin, X-Requested-With, Content-Type, Accept, Authorization'; # 允许的请求头
add_header Access-Control-Max-Age 3600; # 预检请求的缓存时间,单位是秒
# 处理 OPTIONS 请求(用于预检请求)
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Origin, X-Requested-With, Content-Type, Accept, Authorization';
add_header Access-Control-Max-Age 3600;
return 204; # 对预检请求返回 204 No Content
}
access_log /www/wwwlogs/media-access.log;
}
请将这部分代码直接粘贴在你要监听的server模块即可。这样我们可以直接在浏览器进行测试看下能否正常访问。如图所示:
好的,经过一番整理和准备,我们的所有素材基本上已经准备完毕。接下来,我们的重点将转向如何有效地使用这些素材,最大化它们的潜力。
当然,除了那些复杂的方案之外,我接下来还想和大家分享一些云开发中的简单方案,它们可以在实现需求的同时,保持高效和灵活。
云存储
每个套餐都有其固定的存储额度。例如,以我的个人开发版为例,它大约提供了2GB的存储空间,对于大部分日常开发需求来说,这个容量已经足够使用。如果在实际使用过程中,发现存储空间不足以满足需求,也不必担心。你可以按照之前提到的步骤,自行搭建额外的存储方案,以确保能够灵活应对不断增长的数据存储需求。
你可以完全把它当作自己的文件夹来使用。然而需要特别注意的是,云存储平台并不允许修改文件夹或文件的名称。因此,在上传文件之前,除非有特殊需要,否则建议将文件夹或文件的中文名称更改为英文或其他字符。如图所示:
我们所需要的就是这个临时链接,获取到这个链接之后,你就可以正常地使用相关资源,整个过程非常简单直观。除了提供基本的链接功能,云函数还有一个非常重要的特性,那就是它支持缓存设置和CDN加速,这意味着它能够显著提升文件访问的速度和效率,减少延迟,优化用户体验。如图所示:
如何使用
例如,如果我们希望在现有界面中添加一个音乐播放组件,可以参考下方所示的图示:
因此,我们只需要在URL栏中直接输入对应的文件路径,系统便能够自动识别并加载所需资源。具体操作过程请参考下方所示的图示:
如果您已经正确填写了URL路径,但仍然无法正常播放音乐,或者遇到下图所示的错误情况,那么很可能是由于跨域问题导致的。如图所示:
这里还有一个需要特别注意的事项:如果你希望你的音乐能够自动播放(例如我设置的背景音乐),你必须确保音乐播放器的组件是可见的。如果播放器组件被隐藏或未显示,音乐将无法自动播放,如图所示:
使用图片非常简便,你可以将其嵌入到图片容器中,也可以作为背景添加到容器内。如图所示,操作方法直观易懂,灵活性也很高。
这里关于素材没有什么技巧了。我们继续往下看。
新建页面
当然了,我们的应用并非仅仅依赖一个单一的页面来完成整个功能。毕竟,对于一款枪战游戏来说,单一页面的设计远远无法满足需求。我们的游戏页面将包含三个主要部分:首先是Web介绍页,用来展示游戏的基本信息与特色;接下来是地图选择页,玩家可以在此选择自己喜欢的战斗地图;最后是游戏作战页,玩家进入实际的战斗场景。
因此,我们可以利用AI技术进一步优化并生成这些页面,确保每个页面都具备最佳的用户体验和功能性,如下图所示:
生成完成后,我们可以在此处选择对应的页面,并根据需要进行选择性编辑和调整。如下图所示:
如何导入原有页面
在这里,我们遇到了一些问题。例如,如果我已经有一个现成的HTML页面,我该如何将其导入进来呢?目前这里的结构完全是从零开始搭建的,没有任何现成的导入功能。那么,遇到这种情况该怎么办呢?其实,这里有一个解决方案——JSX模块。如图所示:
在使用JSX导入现有页面时,特别需要注意,目标页面必须是一个空页面,不能包含任何组件。原因在于,JSX会自动处理组件的创建和渲染。如果目标页面中已经存在其他组件,可能会引发冲突或影响渲染效果。因此,为了避免不必要的错误或混乱,确保目标页面在导入时是干净、空白的。
接下来,我们将一步一步地演示如何将现有的页面导入到JSX中。为了简化演示过程,我这里使用的是一个原始的HTML文件,而非一个集成了各种前端框架的复杂页面。
下面是原始HTML文件的内容:
当页面中包含了大量常用的 CSS、JavaScript 以及各种 div
容器时,如果我们选择手动一一复制并粘贴到新项目中,这种方式不仅效率低下,而且浪费的时间几乎等同于重新从头开始设计和构建一个全新的页面。在这种情况下,借助智能化工具和 AI 助手显得尤为重要。例如,腾讯的混元 AI 助手,可以显著提高我们的开发效率。
具体操作时,我们只需要向 AI 提供一个参考示例,类似于图示的内容,AI 助手便能够智能地分析、提取并生成所需的代码结构。这不仅节省了大量的时间和精力,还能保证代码的一致性和高效性,极大提高了开发的工作流和质量。
接下来,你需要将完整的 HTML 代码提供给 AI 助手,并且附上相应的 JSX 语法示例。这样,基本上就能够顺利导入并运行。需要特别注意的是,页面中的内容不宜过多,因为过于复杂的内容可能会导致导入过程中出现错误。生成的效果大致如下所示:
如你所见,我这里并没有使用多个组件,只有一个简单的 JSX 组件。那么,问题来了,我们原本的 JavaScript 事件处理和 CSS 样式应该如何处理呢?其实,所有这些都已经在 JSX 中实现了。
通过这种方式,我们不仅可以保留原有的逻辑,还能将 JavaScript 事件和样式直接嵌入到 JSX 代码中,使得整个组件更加紧凑且易于管理。如下图所示:
点击事件
按钮
我们可以为我们的组件添加各种类型的事件响应,而不仅仅局限于按钮。事实上,几乎所有的组件都可以绑定事件处理程序,包括文本框、图片、链接、甚至自定义的组件。例如,一个非常常见的需求是为按钮添加点击事件,使其在点击后能够跳转到指定的内置页面。
图片
不过,值得注意的是,在我们的地图选择页面中,用户的交互方式是通过点击图片来进行选择的。尽管如此,图片依然存在点击事件的触发机制,具体表现如图所示:
页面跳转
关于页面跳转的部分,我先简单说明一下,其他功能暂时还没有涉及。通常情况下,您可以选择在本应用内进行页面跳转操作,具体方式如下图所示:
对于外链的使用,只需通过上方提供的跳转网页功能即可轻松实现跳转操作。
JSX组件
这部分工作完全可以交给AI助手来完成,毕竟我们已经多次向AI助手提问。在每次询问时,云开发助手都会根据我们的要求,重新生成JSX代码,如下图所示。
为了避免丢失当前的进展,如果你遇到满意的情况,请记得按住 CTRL+S 快捷键保存文件。这样做不仅能确保你的工作成果得到及时保留,还能在未来需要时,方便地回滚到保存的源码版本。以下是保存后回滚到历史版本的操作示意图:
按键监听
同样地,针对更复杂的自定义样式和交互需求,除了常规的 JavaScript 代码实现外,我们还可以使用 JSX 来编写各种事件监听器,例如按键事件。虽然官方文档中主要提供了鼠标点击事件的实现方式,但在某些特定场景下,比如在我的游戏中需要通过按下 ESC 键来退出游戏,这类需求无法仅通过点击事件来满足。因此,对于这类特定需求,我们可以直接在 JSX 代码中编写相应的按键监听逻辑。如图所示:
JSX组件语法
在上图中,你可能已经注意到,当我监听到 ESC 按键时,触发了一个方法,直接展示了一个弹窗。这个弹窗是通过内置的 JSX 组件语法实现的。JSX 组件语法有很多种类和功能,能够方便地构建交互式 UI 元素。若想了解更多详细信息,具体的组件使用和 API 文档可以参考以下链接:CloudBase 文档 - 工具 API。
具体有很多语法,如图所示:
每一个都有具体示例demo,照抄就可以了。
游戏视角源码
好的,在这里我将分享一下我目前的游戏视角实现。如果你感兴趣的话,可以深入了解整个游戏流程,包括页面渲染、音效处理、持枪视角等方面的具体实现。以下是相关的代码,展示了我如何通过编程实现这些功能。希望能给你带来一些灵感,若有任何疑问,欢迎一起讨论!
import React, { useEffect, useRef, useState } from 'react';
export default function ImageDisplay(props) {
const containerRef = useRef(null);
const imageRef = useRef(null);
const enemiesContainerRef = useRef(null);
const [enemies, setEnemies] = useState([]);
const [modalImage, setModalImage] = useState(null); // 新增状态,用于控制弹出图片
const audioRef = useRef(null); // 创建音频引用
const audioRef2 = useRef(null); // 创建音频引用
const enemyImages = [
"https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2054301",
"https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2054312"
];
// 鼠标跟随效果
useEffect(() => {
const container = containerRef.current;
const image = imageRef.current;
const enemiesContainer = enemiesContainerRef.current;
let offsetX = 0;
let offsetY = 0;
const handleMouseMove = (e) => {
const containerRect = container.getBoundingClientRect();
const mouseX = e.clientX - containerRect.left;
const mouseY = e.clientY - containerRect.top;
const centerX = containerRect.width / 2;
const centerY = containerRect.height / 2;
offsetX = (centerX - mouseX) * 0.05;
offsetY = (centerY - mouseY) * 0.05;
image.style.transform = `translate(-50%, -50%) translate(${offsetX}px, ${offsetY}px)`;
enemiesContainer.style.transform = `translate(-50%, -50%) translate(${offsetX}px, ${offsetY}px)`;
};
const handleMouseLeave = () => {
image.style.transform = 'translate(-50%, -50%)';
enemiesContainer.style.transform = 'translate(-50%, -50%)';
};
container.addEventListener('mousemove', handleMouseMove);
container.addEventListener('mouseleave', handleMouseLeave);
// 清理事件监听
return () => {
container.removeEventListener('mousemove', handleMouseMove);
container.removeEventListener('mouseleave', handleMouseLeave);
};
}, []);
// 添加敌人
const addEnemy = () => {
const randomEnemyImage = enemyImages[Math.floor(Math.random() * enemyImages.length)];
const containerRect = containerRef.current.getBoundingClientRect();
const imageRect = imageRef.current.getBoundingClientRect();
// 获取容器中心90%区域的大小
const centerWidth = containerRect.width * 0.9;
const centerHeight = containerRect.height * 0.9;
// 计算可放置敌人的范围,使其出现在90%区域内
const maxX = centerWidth - 50; // 减去敌人图标的宽度
const maxY = centerHeight - 50; // 减去敌人图标的高度
const minX = (containerRect.width - centerWidth) / 2;
const minY = (containerRect.height - centerHeight) / 2;
// 生成随机位置,限制在90%区域内
const randomX = minX + Math.random() * maxX;
const randomY = minY + Math.random() * maxY;
const newEnemy = {
id: Date.now(),
image: randomEnemyImage,
x: randomX,
y: randomY
};
setEnemies((prevEnemies) => [...prevEnemies, newEnemy]);
setTimeout(() => {
setEnemies((prevEnemies) => prevEnemies.filter(enemy => enemy.id !== newEnemy.id));
}, 5000);
};
useEffect(() => {
const intervalId = setInterval(addEnemy, 3000);
return () => clearInterval(intervalId);
}, []);
// 点击敌人事件处理
const handleEnemyClick = (enemyId, enemyImage) => {
// 移除敌人
setEnemies((prevEnemies) => prevEnemies.filter(enemy => enemy.id !== enemyId));
// 设置弹出图片并显示 1 秒后自动消失
setModalImage('https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2054324');
// 重置音频播放进度到0
if (audioRef2.current) {
audioRef2.current.currentTime = 0; // 重置音频播放进度
audioRef2.current.volume = 1; // 设置音量为最大
audioRef2.current.play().catch((error) => {
console.error("音效播放失败", error);
});
}
setTimeout(() => {
setModalImage(null); // 1 秒后清除弹出图片
}, 1000);
};
// 监听ESC按键
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === 'Escape') {
// 触发模态弹窗
$w.utils.showModal({
title: '游戏提示',
content: '这是一个模态弹窗',
success(res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
},
});
}
};
window.addEventListener('keydown', handleKeyDown);
// 清理事件监听
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);
// 每次点击鼠标都播放音效
useEffect(() => {
const handleMouseClick = () => {
// 重置音频播放进度到0
if (audioRef.current) {
audioRef.current.currentTime = 0; // 重置音频播放进度
audioRef.current.volume = 1; // 设置音量为最大
audioRef.current.play().catch((error) => {
console.error("音效播放失败", error);
});
}
};
// 在document上监听点击事件
document.addEventListener('click', handleMouseClick);
// 清理事件监听
return () => {
document.removeEventListener('click', handleMouseClick);
};
}, []);
return (
<div
ref={containerRef}
style={{
position: 'fixed', // 使用fixed使其全屏
top: 0,
left: 0,
width: '100vw',
height: '100vh',
overflow: 'hidden', // 禁用滚动条
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f0f0f0',
margin: 0, // 确保没有额外的边距
padding: 0, // 确保没有额外的内边距
boxSizing: 'border-box', // 确保元素大小包括边距和内边距
cursor: 'url(https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2051938), crosshair', // 设置自定义光标
}}
>
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '120%',
height: '120%',
overflow: 'visible',
}}
>
<img
ref={imageRef}
src="https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2051969"
alt="Image"
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '100%',
height: '100%',
transition: 'transform 0.1s ease',
zIndex: -1, // 背景图片
}}
/>
</div>
<div
ref={enemiesContainerRef}
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: '100%',
height: '100%',
overflow: 'visible',
}}
>
{enemies.map((enemy) => (
<div
key={enemy.id}
style={{
position: 'absolute',
width: '80px',
height: '80px',
backgroundImage: `url(${enemy.image})`,
backgroundSize: 'cover',
left: `${enemy.x}px`,
top: `${enemy.y}px`,
cursor: 'url(https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2051938), crosshair', // 保持准星光标
pointerEvents: 'auto', // 确保敌人图标能接收事件
}}
onClick={() => handleEnemyClick(enemy.id, enemy.image)} // 点击敌人
/>
))}
</div>
{/* 弹出图片(点击敌人时展示) */}
{modalImage && (
<img
src={modalImage}
alt="Modal Image"
style={{
position: 'absolute',
top: '85%',
left: '50%',
transform: 'translate(-50%, -85%)',
width: '70px',
height: '70px',
zIndex: 1000, // 确保弹出的图片在最上层
}}
/>
)}
{/* 底部居中的图片 */}
<img
src="https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/resources/2024-12/lowcode-2054553"
alt="Bottom Center Image"
style={{
position: 'absolute',
bottom: '1px', // 固定在底部,距离底部20px
left: '60%',
transform: 'translateX(-60%)', // 使其水平居中
width: '350px', // 固定宽度为50px
height: '350px', // 固定高度为50px
zIndex: 1, // 确保它位于上方
}}
/>
{/* 音效文件,音频元素 */}
<audio ref={audioRef} src="https://域名/media/shut.mp3" />
{/* 音效文件,音频元素 */}
<audio ref={audioRef2} src="https://域名/media/laught.mp3" />
</div>
);
}
除了后面的两个音频文件你需要替换成自己的,其他都是可以直接运行的。
游戏效果
激动人心的时刻终于来临,我的游戏终于完成了!经过无数的设计、开发和调整,最终迎来了这一刻。
体验地址:https://doit-8ghd3vbrc57d4030-1302107156.tcloudbaseapp.com/7h1hex26/preview/?envType=preview
为了更好地展示游戏中的背景音效、页面跳转以及其他互动元素,我特地录制了一个视频。
https://cloud.tencent.com/developer/video/81621
总结
随着游戏开发的旅程接近尾声,我们不禁感慨于腾讯云开发CloudBase平台的强大功能和便捷性。从初步的页面搭建到复杂的游戏视角实现,CloudBase以其AI驱动的可视化开发工具,让整个开发过程变得异常高效。它不仅降低了技术门槛,还极大地提升了开发效率,使得即使是初学者也能快速上手,实现自己的创意。
在这个过程中,体验了从素材准备到页面搭建的每一个环节,感受到了CloudBase在简化开发流程、提供丰富组件和工具方面的优势。无论是图片素材的上传与管理,还是音乐视频素材的集成,CloudBase都提供了直观的操作界面和灵活的解决方案。特别值得一提的是,通过JSX组件和事件监听,我们能够轻松实现复杂的交互逻辑,如按键监听和页面跳转,这些都极大地丰富了游戏的可玩性和用户体验。
通过CloudBase,我们不仅实现了一个简单的2D枪战游戏,更重要的是,我们掌握了一种新的开发方式,这种方式将AI能力与云服务相结合,为未来的项目开发提供了无限可能。
最后,希望通过这篇文章,能够激励更多的开发者加入到CloudBase的行列中来,利用这个平台实现自己的创意和梦想。
我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位腾讯云创作之星、阿里云专家博主、华为云云享专家、掘金优秀作者。
💡 我将不吝分享我在技术道路上的个人探索与经验,希望能为你的学习与成长带来一些启发与帮助。
🌟 欢迎关注努力的小雨!🌟
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
2023-12-13 5分钟搞懂Kubernetes:轻松理解所有组件