Như đã nói ở phần 8, ở bài viết này, chúng ta sẽ sử dụng các kiến thức đã học để thực hành viết ra một script hoàn chỉnh trong 3ds Max. Qua đó, chúng ta sẽ tìm kiểu kỹ hơn về các thành phần giao diện, và cách sử dụng chúng để tương tác với 3ds Max.
Script này sẽ chuyên dùng để đổi tên hàng loạt các vật thể đang được chọn hoặc trong cả scene 3ds Max hiện tại. Mở trình soạn thảo lên và cùng bắt đầu.
MỤC LỤC
Bắt đầu viết script
Ở trong 3ds Max, chúng ta đã có sẵn tính năng tên là Rename Objects (Tools > Rename Object…), dùng để đổi tên hàng loạt các vật thể. Tuy nhiên, nó không có nhiều tùy chọn tương ứng các function có sẵn trong 3ds Max với giá trị String.
Ở đây, chúng ta sẽ viết một script đổi tên mới, đồng thời bổ sung thêm một tính năng mà Rename Objects gốc của 3ds Max không có. Ví dụ, đó là tính năng thay thế ký tự trong tên.
Tạo cửa sổ chính
Trước hết chúng ta sẽ cần thiết kế một cửa sổ cơ bản trong MAXScript.
try destroyDialog RenameObjectsRollout catch() -- thử đóng cửa sổ RenameObjectsRollout rollout RenameObjectsRollout "Đổi tên hàng loạt" ( -- nội dung rollout ) createDialog RenameObjectsRollout 300 60
Sau khi thực thi chuỗi lệnh trên trong MAXScript, trên cửa sổ 3ds Max sẽ xuất hiện hộp thoại có kích thước 300×60 như sau.
Nếu bạn chạy (execute) lại script trên, hộp thoại “Đổi tên hàng loạt” cũ sẽ bị đóng, và hộp thoại mới sẽ mở ra, nhờ vào lệnh try-catch.
Giải thích về try-catch
Lệnh try-catch dùng để thử một khối lệnh nào đó (try), nếu khối lệnh này gây ra lỗi, nó sẽ sử dụng khối lệnh dự phòng (catch) để ngăn 3ds Max báo lỗi. Cụ thể ở đây:
- try destroyDialog RenameObjectsRollout: thử đóng cửa sổ có tên là RenameObjectsRollout. Nếu cửa sổ đó đang mở, lệnh này sẽ đóng nó. Nếu cửa sổ đó không tồn tại, lệnh catch sẽ được hoạt động.
- catch(): khi cửa sổ RenameObjectsRollout không được mở, lệnh destroyDialog RenameObjectsRollout sẽ báo lỗi, và lệnh dự phòng trong catch sẽ được chạy. Ở đây, lệnh dự phòng là (), tức 3ds Max sẽ không làm gì cả. Nó chỉ đơn giản là phòng tránh 3ds Max báo lỗi.
Dòng này sẽ đảm bảo script của chúng ta chỉ tạo ra duy nhất một cửa sổ RenameObjectsRollout trên màn hình 3ds Max.
Giải thích về rollout
Khối lệnh tiếp theo được sử dụng để tạo ra một giao diện người dùng (UI) với một rollout, tên là “RenameObjectsRollout“. Rollout là một dạng giao diện nhỏ, có thể mở rộng và thu nhỏ, thường được sử dụng để chứa các phần tử giao diện khác như nút bấm, văn bản, và các thành phần giao diện khác.
Ở đây, bạn sẽ thấy rollout không thể mở rộng và thu nhỏ được, do chúng được chuyển thành một dialog bằng lệnh creatdialog.
Lưu ý rằng RenameObjectsRollout là tên nội bộ của rollout, được dùng để tham chiếu đến rollout này trong mã nguồn. Tên nội bộ này phải được viết liền, và không chứa ký tự đặc biệt. Đổi tên hàng loạt là tiêu đề của rollout, sẽ hiển thị trên giao diện người dùng.
Bổ sung thành phần giao diện
Để sử dụng được script đổi tên, chúng ta sẽ cần một chỗ nhập liệu và một nút bấm. Hãy thay dòng — nội dung rollout bằng đoạn dưới.
editText newName "Tên mới: " fieldWidth:200 align:#center button renameButton "Đổi tên" align:#center
Trong đây, tôi sử dụng hai thành phần giao diện là edittext và button. Edittext là một hộp văn bản để người dùng nhập tên mới cho các đối tượng. Hộp văn bản này có tiêu đề là Tên mới và chiều rộng là 200 đơn vị. Button là một nút bấm với tiêu đề là Đổi tên. Tất cả chúng đều được căn giữa giao diện bằng thuộc tính align:#center.
Sau khi bổ sung, code của chúng ta sẽ trông như sau:
try destroyDialog RenameObjectsRollout catch() -- thử đóng cửa sổ RenameObjectsRollout rollout RenameObjectsRollout "Đổi tên hàng loạt" ( editText newName "Tên mới: " fieldWidth:200 align:#center button renameButton "Đổi tên" align:#center ) createDialog RenameObjectsRollout 300 60
Chạy thử script này, một hộp thoại với giao diện người dùng cơ bản sẽ xuất hiện.
Viết chức năng cho nút đổi tên
Hiện tại, script mới chỉ có phần “vỏ” và không có bất kỳ tác dụng gì, bởi chúng ta chưa viết chức năng cho nó. Chúng ta sẽ viết chức năng để khi bấm nút “Đổi tên”, script sẽ lấy text từ editext newName để gán cho các vật thể hiện tại.
on renameButton pressed do -- khi bấm nút renameButton ( -- Lấy giá trị nhập vào từ newPrefixEditText if newName.text == "" then messageBox "Chưa nhập tên!" else ( local tempName = newName.text if selection.count > 0 then -- nếu có đối tượng được chọn thì chạy vòng lặp ( for obj in selection do obj.name = tempName -- Chạy vòng lặp for-do để đổi tên đối tượng messageBox "Đổi tên thành công!" -- hiện thông báo đổi tên thành công ) else messageBox "Không có đối tượng nào được chọn." -- nếu không chọn vật gì thì hiện thông báo ) )
Chúng ta sẽ đưa phần code này vào trong dấu đóng mở ngoặc của rollout. Bởi renameButton là một biến local chỉ xuất hiện trong rollout RenameObjectsRollout, nếu đưa ra ngoài, chúng ta sẽ phải sửa dòng đầu tiên thành on RenameObjectsRollout.renameButton pressed do.
Trình bày lại script cho “sạch sẽ”
Script của chúng ta sau khi bỏ tất cả các ghi chú sẽ trông như sau:
try destroyDialog RenameObjectsRollout catch() rollout RenameObjectsRollout "Đổi tên hàng loạt" ( editText newName "Tên mới: " fieldWidth:200 align:#center button renameButton "Đổi tên" align:#center on renameButton pressed do ( if newName.text == "" then messageBox "Chưa nhập tên!" else ( local tempName = newName.text if selection.count > 0 then ( for obj in selection do obj.name = tempName messageBox "Đổi tên thành công!" ) else messageBox "Không có đối tượng nào được chọn." ) ) ) createDialog RenameObjectsRollout 300 60
Hãy cố gắng trình bày code gọn gàng nhất có thể. Đừng viết thừa dấu cách, dấu xuống dòng hay đóng mở ngoặc bừa bãi. Điều này rất quan trọng. Việc trình bày sạch sẽ gọn gàng sẽ giúp bạn đọc lại code dễ dàng hơn và phòng tránh các lỗi tiềm ẩn về cú pháp. Hãy tìm hiểu thêm về Coding Standards nếu bạn có thời gian.
Lưu ý rằng, trên blog này, vì giới hạn của trình soạn thảo, tôi đã phải sử dụng dấu cách để thêm các khoảng lùi đầu dòng (indentation). Nhưng trong trình soạn thảo MAXScript Editor, hãy dùng phím Tab, thay vì dùng dấu cách.
Chạy thử script
Chúng ta sẽ thử xem script trên đã hoạt động chưa. Mở script lên (execute), và sau đó bấm nút “Đổi tên”. Thông báo lỗi “Chưa nhập tên” sẽ hiện ra bởi chúng ta chưa nhập tên gì.
Chúng ta sẽ nhập chữ “KhoiBox” vào trong ô “Tên mới”, sau đó lại bấm nút “Đổi tên”. Thông báo lỗi chưa chọn đối tượng sẽ hiện ra vì chúng ta chưa chọn đối tượng nào.
Vậy là hai câu lệnh điều kiện kiểm soát lỗi (if-then) của chúng ta đã hoạt động tốt. Bây giờ, hãy 10 khối box trong scene hiện tại (để tăng tính thử thách, hãy thử suy nghĩ làm công việc đó tự động bằng MAXScript). Sau đó chọn tất cả chúng và bấm “Đổi tên”.
Lần này, mọi thứ đã hoạt động trơn tru, và thông báo “Đổi tên thành công!” đã hiện ra. Tuy nhiên chúng ta có nên dừng lại ở đây không?
Sửa lỗi script
Hãy thử kiểm tra các khối box. Bạn sẽ thấy chúng giống hệt nhau là “KhoiBox”. Việc trùng lặp này có thể dẫn đến một số vấn đề trong 3ds Max, nhất là với các script có sử dụng tính năng getNodeByName. Chúng ta sẽ khắc phục điều này bằng cách thêm số đuôi (suffix number) cho tên của nó, dạng “001”, “002”…
Để làm được điều này, chúng ta sẽ cần một function có tên là uniqueName. Function này sẽ tự động thêm các số đuôi cho tên, để đảm bảo chúng không trùng lặp với các tên khác có sẵn trong scene hiện tại. Chúng ta sẽ sửa vòng lặp như sau:
for obj in selection do obj.name = uniqueName tempName
Sau đó chạy lại script. Điền “KhoiBox” vào trong ô nhập tên, và bấm nút “Đổi tên”. Hãy kiểm tra các khối box, lần này chúng đã được đổi tên thành “KhoiBox001”, “KhoiBox002”, …, “KhoiBox010”. Tuyệt vời, chúng ta đã hoàn thành xong script đầu tiên.
Bài tập thực hành
Trong script trên, chúng ta mới chỉ đổi tên của vật thể đơn thuần. Hãy sử dụng các kiến thức đã học được trong phần 8 về String, để thêm chức năng cho script này. Như các phần trước, dùng chuột bôi đen phần phía dưới mỗi đề bài để thấy đáp án. Nếu có thể, hãy thử nghĩ ra một cách giải khác.
Bài tập số 1
Hãy thêm một nút bấm có tên là “Rename All”. Khi bấm nút này, thay vì đổi tên những gì đang chọn, nó sẽ đổi tên toàn bộ đối tượng trong 3ds Max. Gợi ý: thay vì dùng selection, hãy dùng biến objects cho vòng lặp for-do.
if objects.count > 0 then ( for obj in objects do obj.name = tempName messageBox "Đổi tên thành công!" )
Bài tập số 2
Hãy thêm một nút bấm có tên là “Prefix” để khi bấm nút này, script sẽ thêm những gì người dùng đã nhập vào ô “Đổi tên” vào phía trước tên hiện tại của vật thể. Ví dụ, tên vật thể đang là “Test”, người dùng nhập “Fix”, thì tên cuối sẽ là “FixTest”.
-- sửa vòng lặp thành for obj in selection do obj.name = uniqueName (tempName + obj.name)
Bài tập số 3
Hãy thêm một nút bấm có tên là “Remove”. Khi bấm nút này, script sẽ tìm những ký tự tương ứng trong tên vật thể và xóa chúng đi. Ví dụ, tên vật thể đang là “FixTest”, người dùng nhập “Fix”, thì tên cuối sẽ là “Test”.
-- sửa vòng lặp thành for obj in selection do ( k = findString obj.name tempName if k != undefined then obj.name = uniqueName (replace obj.name k tempName.count "") )